Skip to content

fix(meshcore): surface real Share Contact errors instead of failing silently#3481

Merged
Yeraze merged 1 commit into
mainfrom
fix/share-contact-meshcore-tcp
Jun 15, 2026
Merged

fix(meshcore): surface real Share Contact errors instead of failing silently#3481
Yeraze merged 1 commit into
mainfrom
fix/share-contact-meshcore-tcp

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

Share Contact did nothing on a MeshCore TCP Companion source and gave the user no actionable error (issue #3480). I traced the full chain and the root cause is broader than the issue described: meshcore.js's shareContact() rejects with no argument on a firmware Err and has no internal timeout (connection.js:1287), so the propagated error was the literal string "undefined" — or, if the firmware never replied, a 30s hang followed by a generic timeout. On top of that the manager returned a bare boolean, the route replied with a hardcoded 409 string, and the UI showed a hardcoded i18n string, so the real reason never reached the user.

The device-type gate is not the cause — a TCP Companion correctly resolves to COMPANION, and the on-wire frame (opcode 16 + 32-byte key) is correct. If the broadcast itself fails, that's almost certainly a firmware limitation (pymc / official v1.15 not implementing CMD_SHARE_CONTACT); this PR makes that observable and fast rather than silent, which is the actual complaint.

Changes

  • Native backend (meshcoreNativeBackend.ts): wrap c.shareContact() and convert the argument-less reject() into an actionable error (with cause) instead of letting String(undefined) propagate.
  • Manager (meshcoreManager.ts): shareContact() now returns { ok, error } (ShareContactResult), uses a 10s dedicated timeout instead of the 30s default (fail fast, no hang), and maps timeout / "undefined" into firmware-support hints.
  • Route (meshcoreRoutes.ts): forward the real error text — 504 for no-response, 409 otherwise.
  • UI (useMeshCore.ts hook + MeshCoreContactDetailPanel.tsx): surface the server's error text instead of the hardcoded generic string.

Issues Resolved

Fixes #3480.

Documentation Updates

No documentation changes needed — internal error-handling / diagnostics.

Testing

  • Full Vitest suite: 7035 tests, 0 failures
  • TypeScript compiles cleanly (tsc --noEmit)
  • New meshcoreManager.shareContact.test.ts (6): return shape + short timeout, non-Companion, disconnected, timeout→hint, "undefined"→hint, verbatim forward
  • meshcoreNativeBackend.test.ts (+2): success path + bare-reject()→actionable error
  • meshcoreRoutes.test.ts: updated mock shape; 409 forwards real error + new 504 timeout case
  • MeshCoreContactDetailPanel.test.tsx (+2): real error surfaced + generic fallback
  • Manual: on a MeshCore TCP Companion (pymc / v1.15), Share Contact now shows a specific reason within ~10s instead of hanging/failing silently

🤖 Generated with Claude Code

…ilently

Share Contact did nothing on a MeshCore TCP Companion source with no
actionable error (issue #3480). Root cause spans the chain: meshcore.js's
shareContact() rejects with NO argument on a firmware Err and has no internal
timeout, so the propagated error was the literal string "undefined" (or, with
no firmware response, a 30s hang then a generic timeout). The manager returned
a bare boolean, the route replied with a hardcoded 409 string, and the UI
showed a hardcoded i18n string — so the real reason never reached the user.

- Native backend: wrap c.shareContact() and convert the argument-less reject
  into an actionable message ("firmware may not support CMD_SHARE_CONTACT").
- Manager: shareContact() returns { ok, error } (ShareContactResult), uses a
  10s dedicated timeout instead of the 30s default (fail fast, no hang), and
  maps timeout / "undefined" into firmware-support hints.
- Route: forward the real error; 504 for no-response, 409 otherwise.
- UI hook + panel: surface the server's error text instead of a generic string.

Note: the underlying broadcast may still fail if the firmware (pymc / v1.15)
doesn't implement CMD_SHARE_CONTACT — this makes that observable and fast
rather than silent. Tests cover backend, manager, route, and panel layers.

Fixes #3480

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: Share Contact Error Handling Improvements

Review Checklist

  • Read repository guidelines and context
  • Review core changes in native backend and manager
  • Analyze error handling flow and timeout logic
  • Review UI changes and error surfacing
  • Examine test coverage and quality
  • Check for security and performance considerations
  • Provide overall assessment and recommendations

Overall Assessment

This is an excellent PR that addresses a significant UX issue (#3480) with a comprehensive, well-architected solution. The changes follow MeshMonitor's coding standards and demonstrate careful attention to error handling, testing, and user experience.

✅ Strengths

1. Comprehensive Error Chain Fix

The PR traces and fixes the entire error propagation chain from the lowest level (meshcore.js) to the UI:

  • Native Backend (meshcoreNativeBackend.ts:894-908): Wraps the bare reject() with actionable error messages
  • Manager (meshcoreManager.ts:2077-2106): Adds 10s timeout and maps common failure modes to user-friendly text
  • Routes (meshcoreRoutes.ts:723): Properly differentiates 504 (timeout) vs 409 (rejection) status codes
  • UI (MeshCoreContactDetailPanel.tsx:344-347): Surfaces real server errors instead of generic i18n fallback

2. Smart Timeout Strategy

  • Reduces timeout from 30s to 10s for share-contact specifically
  • Fails fast on firmware that doesn't support the command instead of hanging
  • Timeout hints help users understand firmware limitations rather than generic failure

3. Excellent Test Coverage

  • 6 new tests in meshcoreManager.shareContact.test.ts covering all error scenarios
  • 2 new native backend tests for success and bare-reject cases
  • Route tests updated to verify new status codes and error forwarding
  • UI tests verify both real error display and generic fallback

4. Type Safety & Structure

  • Clean ShareContactResult interface with { ok: boolean; error?: string }
  • Consistent error handling pattern across the codebase
  • Proper async/await usage throughout

🔍 Technical Details

Error Handling Flow

  1. meshcore.js rejects with undefined on firmware error
  2. Native backend catches this and throws descriptive error with cause
  3. Manager applies 10s timeout and maps common patterns to user hints
  4. Routes return appropriate HTTP status (504 for timeout, 409 otherwise)
  5. UI displays the real error text instead of hardcoded fallback

Performance Improvements

  • 10s timeout instead of 30s reduces hang time by 66%
  • Fast failure on unsupported firmware improves UX responsiveness

⚠️ Minor Observations

1. Error Message Consistency

The timeout error message in the manager (meshcoreManager.ts:2092) could be more consistent with the native backend format, but both are clear and actionable.

2. Test Mock Pattern

The private method stubbing approach in tests is well-executed and follows existing patterns in the codebase (meshcoreManager.channels.test.ts).

🛡️ Security & Performance

  • No security concerns: Error handling improvements don't expose sensitive data
  • Performance positive: Reduced timeout decreases resource usage
  • Defensive coding: Proper null checks and type guards throughout

📋 Code Quality

  • Follows repo conventions: Uses established patterns from CLAUDE.md
  • Clean separation: Each layer handles appropriate concerns
  • Documentation: Comprehensive JSDoc comments explaining the firmware quirks
  • TypeScript: Proper typing and interface definitions

🎯 Recommendation

APPROVE - This PR is ready to merge. It:

  1. ✅ Solves the reported issue completely
  2. ✅ Follows established architectural patterns
  3. ✅ Includes comprehensive test coverage
  4. ✅ Improves both UX and performance
  5. ✅ Maintains backward compatibility
  6. ✅ Has clean, well-documented code

The solution is thoughtful, well-tested, and addresses the root cause rather than just symptoms. The error handling improvements will benefit users across different firmware versions and connection types.


Great work on this comprehensive fix! The attention to detail in tracing the entire error chain and providing actionable feedback to users is exemplary.

@Yeraze Yeraze merged commit 522d0ad into main Jun 15, 2026
19 checks passed
@Yeraze Yeraze mentioned this pull request Jun 15, 2026
Yeraze added a commit that referenced this pull request Jun 15, 2026
Bump version to 4.10.4 across package.json, package-lock.json,
helm/meshmonitor/Chart.yaml, desktop/package.json, and
desktop/src-tauri/tauri.conf.json.

Promote CHANGELOG [Unreleased] to [4.10.4] and add the user-facing fixes
merged since 4.10.3: timed-events cross-source firing (#3479), module
config save for Traffic Management/Status Message (#3464), Auto Favorites
firmware-support cache (#3482), MeshCore Share Contact error surfacing
(#3481), and the Firefox-Android delivered-icon CSS fix (#3477). The
air-quality particle-count fix (#3483) was already in [Unreleased].

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Share Contact silently fails for MeshCore TCP (Companion) sources

1 participant