Typed native command errors
July 17, 2026 ยท View on GitHub
Status: accepted
Date: 2026-07-14
Context
Tauri commands previously rejected with display strings. That made any frontend recovery decision depend on human-readable prose and made a copy edit capable of changing control flow. Structured Tauri rejections also arrive in JavaScript as plain objects, so changing only the Rust return type would degrade existing String(error) presentation to [object Object].
Decision
Every fallible Tauri command returns a serializable CommandError with a stable snake-case code and a bounded human-readable message. apps/desktop/native-command-error-codes.json is the complete shared vocabulary; Rust and TypeScript derive or test against it instead of duplicating the list in prose. Internal Rust helpers may retain richer domain errors or Result<_, String> while they are not an IPC contract; the outer command assigns the narrowest stable category and otherwise uses internal_error. Codes are never inferred by matching old message text. Fallible commands use #[typed_tauri_command::command]; its procedural macro rejects any return type other than the canonical crate::command_error::CommandResult<T> during Rust compilation. Infallible commands continue to use Tauri's ordinary attribute.
Every frontend command call goes through invokeNative. It validates the rejection shape, bounds the message, produces a real NativeCommandError, and maps legacy strings or malformed values to a fixed internal failure. Frontend control flow branches only on the validated code. Copy remains presentation and may change independently.
Invite onboarding follows the same rule locally: InviteJoinError.code classifies legacy, expired, invalid, and host-binding failures; its message is not a recovery contract.
Consequences
- Adding or changing a branchable code is a compatibility decision with Rust serialization and TypeScript normalization tests.
- Unknown or malformed native errors fail to
internal_error; they do not expose object serialization or become new behavior through prose. - Sensitive native causes stay behind the IPC boundary. Explicitly classified errors may carry bounded display copy; unclassified string causes become fixed internal copy rather than a diagnostic dump.
- Existing internal helpers can migrate incrementally without reintroducing string matching at command or UI boundaries.
Revisit when
Revisit the taxonomy when a new frontend recovery path cannot be expressed by the existing codes. Add the narrowest semantic code and tests on both sides of IPC; do not create command-specific prose parsers or a generic unvalidated string-code escape hatch.