Skip to content

Refactor Rust SDK errors to use structs with a kind() method#1400

Open
heaths wants to merge 4 commits into
github:mainfrom
heaths:error-struct
Open

Refactor Rust SDK errors to use structs with a kind() method#1400
heaths wants to merge 4 commits into
github:mainfrom
heaths:error-struct

Conversation

@heaths
Copy link
Copy Markdown

@heaths heaths commented May 23, 2026

The conventional #[non_exhaustive] enum Error { ... } pattern appears
safe but creates problems as a library evolves. This PR changes all
error types to the struct-with-kind() pattern, which also aligns with
the Azure SDK for Rust error design.

Why not a flat error enum:

  • #[non_exhaustive] on the enum prevents exhaustive matching, but
    individual variants are still fixed. Adding a field to any variant —
    even just to improve an error message with a line number or file path
    — is a breaking change.
  • Adding context data is harder than it looks. With a flat enum, new
    fields touch every affected variant and all match arms across the
    codebase. With a struct, new fields are added in one place and callers
    who don't use them are unaffected.
  • A single enum conflates all failure modes, making it impossible to
    document or guarantee which variants a given function can actually
    return. Callers must handle unrelated variants they will never see, or
    accept a wildcard arm that silently swallows future additions.

The struct + kind pattern:

Concern Flat enum Struct + kind()
Categorization Match directly on variant Call .kind()&*Kind
Adding context Breaking: add fields to variant Non-breaking: add fields to struct
non_exhaustive On enum; variants are fixed Not needed on struct with only private fields
Simple display Must match all variants format!("{err}") — no match needed

Callers who only want to display or propagate an error with ? do not
need to call .kind() at all. Only callers who need to inspect the
failure category call .kind(), and they get a stable, scoped *Kind
enum to match against.

The conventional `#[non_exhaustive] enum Error { ... }` pattern appears
safe but creates problems as a library evolves. This PR changes all
error types to the struct-with-`kind()` pattern, which also aligns with
the Azure SDK for Rust error design.

Why not a flat error enum:

- `#[non_exhaustive]` on the enum prevents exhaustive matching, but
  individual variants are still fixed. Adding a field to any variant —
  even just to improve an error message with a line number or file path
  — is a breaking change.
- Adding context data is harder than it looks. With a flat enum, new
  fields touch every affected variant and all match arms across the
  codebase. With a struct, new fields are added in one place and callers
  who don't use them are unaffected.
- A single enum conflates all failure modes, making it impossible to
  document or guarantee which variants a given function can actually
  return. Callers must handle unrelated variants they will never see, or
  accept a wildcard arm that silently swallows future additions.

The struct + kind pattern:

| Concern | Flat enum | Struct + `kind()` |
|---|---|---|
| Categorization | Match directly on variant | Call `.kind()` → `&*Kind` |
| Adding context | Breaking: add fields to variant | Non-breaking: add fields to struct |
| `non_exhaustive` | On enum; variants are fixed | Not needed on struct with only private fields |
| Simple display | Must match all variants | `format!("{err}")` — no match needed |

Callers who only want to display or propagate an error with `?` do not
need to call `.kind()` at all. Only callers who need to inspect the
failure category call `.kind()`, and they get a stable, scoped `*Kind`
enum to match against.
@heaths heaths requested a review from a team as a code owner May 23, 2026 03:29
Copilot AI review requested due to automatic review settings May 23, 2026 03:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors the Rust SDK error model to a structured Error/ErrorKind design (and similar *Kind + error wrapper types), removing thiserror usage and updating call sites, tests, examples, and docs accordingly.

Changes:

  • Introduces rust/src/errors.rs with ErrorKind, ProtocolErrorKind, SessionErrorKind, and a shared internal representation for error messages/sources.
  • Updates SDK code/tests/examples to match on *.kind() and construct errors via with_message(...) / From<...> conversions.
  • Adjusts test configuration to require test-support for selected integration tests; updates editor/LSP configuration.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
rust/tests/session_test.rs Updates session tests to assert on err.kind() and to_string() instead of enum variants.
rust/tests/protocol_version_test.rs Updates protocol version tests to match on ErrorKind::Protocol(...).
rust/tests/e2e/session_fs_sqlite.rs Migrates FS error construction to new FsError/FsErrorKind API.
rust/src/types.rs Switches duplicate tool handler errors to Error::with_message(ErrorKind::InvalidConfig, ...).
rust/src/tool.rs Updates tool tests to match on err.kind() (ErrorKind::Json).
rust/src/subscription.rs Introduces RecvErrorKind + RecvError wrapper and manual Display/Error impls.
rust/src/session_fs.rs Reworks FS errors into FsErrorKind + FsError wrapper with wire mapping logic.
rust/src/session.rs Constructs/propagates session errors via ErrorKind::Session(SessionErrorKind::...).
rust/src/resolve.rs Returns BinaryNotFound via ErrorKind (with optional hint) and updates docs.
rust/src/lib.rs Moves crate error types out to errors.rs, re-exports them, and updates many signatures to Result<T>.
rust/src/jsonrpc.rs Updates JSON-RPC parsing/transport errors to new ErrorKind model.
rust/src/errors.rs Adds new centralized error system (Error, ErrorKind, ProtocolErrorKind, SessionErrorKind, StopErrors, etc.).
rust/src/embeddedcli.rs Migrates embedded CLI extraction/install errors off thiserror to internal Repr model.
rust/examples/session_fs.rs Updates example to construct FsError via FsErrorKind.
rust/examples/manual_tool_resume.rs Adjusts imports due to subscription error type visibility changes.
rust/Cargo.toml Removes thiserror; gates selected integration tests behind test-support.
.vscode/settings.json Configures rust-analyzer to use all features and clippy checks.
.github/skills/rust-coding-skill/SKILL.md Updates Rust skill guidance to reference ErrorKind vs prior Error enum variants.
.github/lsp.json Adds rust-analyzer to repo LSP configuration.
.github/copilot-instructions.md Adds guidance to use configured LSP operations (find refs/rename) instead of pattern matching.

Comment thread rust/src/errors.rs
Comment thread rust/src/errors.rs Outdated
Comment thread rust/src/subscription.rs Outdated
Comment thread rust/src/subscription.rs
Comment thread .vscode/settings.json Outdated
Comment thread .github/skills/rust-coding-skill/SKILL.md Outdated
Comment thread rust/src/errors.rs Outdated
heaths added 3 commits May 22, 2026 20:42
Sticking with `*Kind` as a convention for error enums.
Enhanced the Error struct to include an optional backtrace, which is captured only when `RUST_BACKTRACE` is set. This change helps in debugging by providing context on error occurrences without inflating the Error size unnecessarily.
@stephentoub stephentoub requested a review from tclem May 23, 2026 12:38
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.

2 participants