Skip to content

Duplicating an underlying error in both the wrapper message and its source #11

Open

Description

@cblanquera

Timing: Follow-up after trial/3 merges into main; defer implementation until then.

Review baseline: Local trial/3 at a094418baf6388e3eeca23821df481a7f0a8751c. This branch is not yet on the remote. Source locations below refer to that snapshot and should be rechecked after merge.

Problem

Error::from_source copies the underlying error's display text into the wrapper message and also retains the same error as its source. Source-chain formatting then repeats the same explanation rather than adding context.

Evidence

  • crates/lib/src/error.rs:224-229: message is derived from cause.to_string() and the cause is retained.
  • crates/idea/idea-transformer/src/lib.rs:77-83: parser-error conversion uses the same pattern.
  • tools/cli/src/diagnostics.rs:47-58: developer formatting prints the wrapper and every cause.
  • A controlled probe confirmed wrapper Display and source Display both equal disk unavailable, producing a diagnostic such as disk unavailable; caused by: disk unavailable.

Acceptance criteria

  • Establish a consistent error-wrapping policy: outer errors add distinct context, while source traversal carries the underlying explanation without redundant repetition.
  • Preserve concrete causes/downcasting, origin metadata, logical invocation frames, and public/private disclosure boundaries.
  • Review relevant wrappers and verbose formatters together; do not remove causes merely to hide duplicated text.
  • Verify representative IO/parser and multi-layer diagnostics.

Code examples and suggested direction

At a wrapping boundary, add distinct context while retaining the concrete cause:

use stackpress_lib::{Error, ErrorKind};

let cause = std::io::Error::other("disk unavailable");
let error = Error::new("bootstrap configuration could not be read")
    .with_kind(ErrorKind::External)
    .with_cause(cause);

Expected developer chain:

bootstrap configuration could not be read; caused by: disk unavailable

This illustrates contextual wrapping. The generic from_source conversion still needs an explicit compatibility policy; changing its existing Display text can affect callers and tests.

Activity

  1. cblanquera commented on Oct 26, 2026

    @cblanquera
    MemberAuthor

    Suggested investigation and fix notes

    Deferred until trial/3 is merged into main. Recheck the merged source and tests first. The snippets are examples/proposals, not an approved patch or a change to frozen contracts.

    1. Inventory Error::from_source, From<serde_json::Error>, From<io::Error>, transformer/parser conversions, and the CLI's bounded source-chain formatter.
    2. Decide the distinction between a contextual wrapper and a transparent/pass-through conversion. Contextual wrappers should explain the failed operation; the source retains the original explanation. Document compatibility implications before changing generic Display/message behavior.
    3. Fix construction/formatting policy rather than deduplicating arbitrary equal strings in the reporter: two different errors can legitimately share text.
    4. Preserve sources/downcasting, source locations, invocation frames, and public disclosure policy. Never remove the source merely to shorten output.
    5. Cover a single IO error, parser coordinates, multiple distinct context layers, and equal-text but distinct failures. Preserve the CLI's finite traversal of cyclic/deep application-defined chains.

    Example check for the contextual form:

    use std::error::Error as _;
    
    assert_eq!(error.to_string(), "bootstrap configuration could not be read");
    let cause = error.source().unwrap();
    assert!(cause.downcast_ref::<std::io::Error>().is_some());
    assert_eq!(cause.to_string(), "disk unavailable");

    Start with Lib request/response diagnostic tests, transformer composition errors, and CLI entry contracts. Reference: standard Error source guidance.

    Implementation workflow: After the merge, follow .agents/workflows/rust-programming.md: prove the affected behavior, complete the three test-verified review rounds, and record relevant formatting/lints/docs, feature/platform scope, and applicable development coverage. Preserve unrelated changes.

  2. cblanquera commented on Oct 26, 2026

    @cblanquera
    MemberAuthor

    Fixed on trial/3 in 644795f (merged as 64a5b70, task 00010 lane 1; not yet on main, so leaving open).

    • Wrappers now add distinct context and keep the concrete cause in source(); the cause's text is no longer copied into the wrapper.
    • Approved Display change: generic Error::from_source now shows external operation failed; concrete conversions show JSON operation failed / I/O operation failed / integer parsing failed. Known operations use Error::new(context).with_kind(..).with_cause(cause). Documented in Rustdoc and crates/lib/docs/api/error.md.
    • Downcasting, origins, invocation frames and public disclosure are preserved; developer formatters print each layer once.
    • Regression error_contracts failed first (wrapper Display was disk unavailable; a three-layer chain repeated its text), now passes.
    • Server/CLI/transformer callers adopt the same policy in lane 2's follow-up.

    Coordinator-verified: fmt, Clippy -D warnings, Rustdoc -D warnings, cargo +1.96.0 check, 454 workspace tests, 856 doctests pass; lib production coverage 93.64%.

  3. added 4 commits that reference this issue on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions