Skip to content

feat(errors): add shared user cancellation error - #1986

Open
aidandaly24 wants to merge 6 commits into
aws:refactorfrom
aidandaly24:feat/user-cancellation-error
Open

feat(errors): add shared user cancellation error#1986
aidandaly24 wants to merge 6 commits into
aws:refactorfrom
aidandaly24:feat/user-cancellation-error

Conversation

@aidandaly24

@aidandaly24 aidandaly24 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Problem

Commander exits and user cancellations bypass the shared CLI error model. Runtime and Gateway invoke define resource-specific interruption errors, while Project dev defines a separate command interruption type for the same user cancellation outcome.

Solution

  • classify CommanderError in AgentCoreCLIError.fromError, preserving help as exit 0 and mapping parse failures to usage exit 2
  • add an opt-in SilentCLIError category so only intentionally silent errors skip generic root stderr output
  • add a silent, user-sourced UserCancellationError with exit code 130
  • centralize process SIGINT listener lifecycle in withUserCancellation for Runtime invoke, Gateway invoke, dataset get, and dataset update
  • use the shared cancellation error as each operation's AbortSignal.reason, including Project dev
  • preserve Project dev's SIGINT/SIGTERM handling, single Shutting down… message, repeated-signal behavior, and listener cleanup
  • preserve Runtime and Gateway partial-response interruption summaries while propagating the original typed cancellation reason
  • leave TUI-local cancellation and low-level platform AbortError handling unchanged

Verification

  • focused cancellation suites across errors, root handling, Runtime, Gateway, Project dev, and datasets (146 pass, 0 fail)
  • full local source suite (1501 pass, 0 fail)
  • bun run typecheck
  • bun run lint:check
  • Prettier check for every changed file
  • bun run build
  • git diff --check
  • GitHub full unit suites:
    • Linux: 1501 pass, 0 fail
    • Windows: 1501 pass, 0 fail
    • macOS: 1501 pass, 0 fail
  • Linux, Windows, and macOS builds
  • AgentCore E2E CodeBuild
  • bundled CLI smoke checks:
    • nested help: exit 0, help on stdout, no stderr or error log
    • nested unknown option: exit 2, one Commander error line, classified as a user error

@github-actions github-actions Bot added agentcore-harness-reviewing AgentCore Harness review in progress and removed agentcore-harness-reviewing AgentCore Harness review in progress labels Aug 12, 2026
@codecov-commenter

codecov-commenter commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.05%. Comparing base (a317a83) to head (2e3832f).

Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #1986      +/-   ##
============================================
- Coverage     97.06%   97.05%   -0.01%     
============================================
  Files           374      374              
  Lines         22542    22514      -28     
============================================
- Hits          21880    21852      -28     
  Misses          662      662              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aidandaly24
aidandaly24 marked this pull request as ready for review August 12, 2026 21:19
@aidandaly24
aidandaly24 marked this pull request as draft August 13, 2026 00:17
@aidandaly24
aidandaly24 marked this pull request as ready for review August 13, 2026 00:22
@aidandaly24
aidandaly24 requested a review from Hweinstock August 13, 2026 16:31

@Hweinstock Hweinstock left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i like the approach! few comments, but I think some could be follow-ups I could help pick up.

Comment thread src/errors/errors.tsx
export class RuntimeInvokeResponseError extends AgentCoreCLIError {
readonly reported = true;

export class RuntimeInvokeResponseError extends SilentCLIError {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why are runtime invoke responses silent? I thought this was the error we get when the stream parsing fails.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is the error we get when response streaming fails. By the time it reaches the root, writeStreamingResponse has already written the sanitized incomplete response summary to stderr. Making this error silent just prevents a second generic Error: response stream failed line. It still goes through structured logging and telemetry.

Comment thread src/runnable/index.tsx
if ((error as Error)?.name === "AbortError") return ExitCode.INTERRUPTED;
if (caught instanceof AgentCoreCLIError) return caught.exitCode;
return ExitCode.FAILURE;
const error = AgentCoreCLIError.fromError(caught);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice, really like how simple this is now!

Comment thread src/index.ts
error_name: error.name,
error_source: error.source,
});
if (error.exitCode !== 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i wonder if it makes sense to expand the exit_reason attribute to accept a cancelled value. That way we still get telemetry for these cancellations.

could be a follow-up since we'll need to adjust the backend schema to accommodate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think cancelled would be clearer. Right now cancellation still emits telemetry as failure with error_source: user. I kept the new exit reason out of this PR since it also requires a backend schema change, so I think that should be a follow-up.

Comment thread src/handlers/eval/dataset/get/index.tsx Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

do we need the same controller.signal.throwIfAborted(); check here?

Also wondering if it makes sense to build an abstraction for this since it seems there's already a few consumers?

Something like:

 async function withCancellation<T>(fn: (signal: AbortSignal) => Promise<T>): Promise<T> {
    const controller = new AbortController();
    const interrupt = () => controller.abort(new UserCancellationError());
    process.once("SIGINT", interrupt);
    try {
      return await fn(controller.signal);
    } catch (error) {
      controller.signal.throwIfAborted();
      throw error;
    } finally {
      controller.abort();
      process.off("SIGINT", interrupt);
    }
  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. After rebasing, Gateway invoke and dataset update introduced two more consumers, so the abstraction makes sense now. I added withUserCancellation under src/runnable and moved Runtime invoke, Gateway invoke, dataset get, and dataset update onto it. It owns the SIGINT listener, cleanup, and throwIfAborted() normalization, while TUI-local cancellation stays separate. I'll do the same with dev when its merged.

@aidandaly24
aidandaly24 force-pushed the feat/user-cancellation-error branch from 5e13303 to 128e4f1 Compare August 17, 2026 17:35
@aidandaly24
aidandaly24 force-pushed the feat/user-cancellation-error branch from 5c7c41f to 2e3832f Compare August 17, 2026 20:54
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.

3 participants