Skip to content

fix(native-chat): report structured agent-session failures instead of discarding them - #20542

Closed
brennanb2025 wants to merge 8 commits into
mainfrom
brennanb2025/fix-structured-onerror-wiring
Closed

brennanb2025 wants to merge 8 commits into
mainfrom
brennanb2025/fix-structured-onerror-wiring

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​222 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​220
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​115 $\color{#cf222e}{\Huge{\mathbf{−}}}$​78 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​37

ELI5

When something goes wrong inside a native chat session — cleaning up after an agent process dies, for example — the code reports the error by calling an error handler that the app is supposed to provide.

Nobody ever provided one. The call was written as "call the handler if there is one," and there never was one, so every single error reported this way was thrown into nothing and disappeared.

This PR provides the handler, and points it at the diagnostic log file Orca already writes.

What Changed

  • The place that sets up native chat sessions now passes an error handler, which routes failures into Orca's existing diagnostic trace file (main.trace.ndjson).
  • The individual report sites now fall back to printing the error if no handler was provided, so a future missing handler degrades to "noisy" instead of "silent."
  • Moved some type definitions into their own file to keep the main one under its line limit.

Why

A user reported a chat that became permanently unusable after an update. When we went looking for the cause, there was nothing to find — zero trace across nine log rotations in two separate profiles. The failure that caused it reported itself correctly, into a handler that didn't exist.

That meant we couldn't diagnose that incident, and wouldn't have been able to diagnose the next one either.

One detail worth knowing: the file that sets up these sessions has type-checking disabled, so the compiler couldn't see the missing handler. Making the handler required wouldn't have helped for the same reason — a missing optional field is legal, and type-checking is off there regardless. A runtime assertion would have caught it, but at the cost of updating roughly eleven test setups. The fallback plus real wiring removes the silent-discard problem more cheaply.

Linked Issue

No tracking issue — found while investigating a reported P0.

Visual Proof

N/A — this changes nothing a user sees. It changes whether we can see anything after a failure.

Testing

The test asserts the real artifact rather than a mock: it triggers the error handler, then reads the actual main.trace.ndjson file after its normal flush and checks the scope, the message, and the stack trace are all there.

  • 4 files, 45 tests passed
  • pnpm tc, check:code-quality:changed (0 new findings across 5 files), pnpm format — all clean

Known gap, disclosed: the desktop app sets up this log file at startup; the background daemon (orcad) does not. So failures on the daemon side still won't be traced until that's wired up too. Out of scope here, worth a follow-up.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Review

Agent skill upstream boundary

  • Not applicable.

Notes

Cross-platform: no platform-specific code. SSH/remote: this is local error reporting and publishes nothing over the wire. Mobile: unaffected. Backwards compatible: the handler is still an optional field, so no existing caller breaks. Performance: one log entry per error, on a path that previously did nothing at all.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: c550bb31-1538-4a72-a6a9-51504c84f037

📥 Commits

Reviewing files that changed from the base of the PR and between 9320989 and 05e315d.

📒 Files selected for processing (3)
  • src/main/runtime/orca-runtime-get-worktree-ps.ts
  • src/main/runtime/structured-agent-session-runtime-deps.ts
  • src/main/runtime/structured-agent-session-runtime.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The change adds and re-exports a shared runtime dependency type. Runtime failures use deps.onError or console.error, with guarded reporting failures. Installer errors create failed agent-session.error spans. Tests cover reporter modes and persisted trace output.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 05e31

If a configured diagnostic reporter fails, the underlying structured-session error may be absent from fallback logs. Resolve or explicitly accept this diagnostic gap before merging.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is otherwise complete and covers the change, motivation, testing, compatibility, scope, and checklist items. However, the required Linked Issue section states that no tracking issue ex… Add a valid linked issue reference under Linked Issue, such as "Fixes #". If no issue exists, create one or obtain an approved repository exception.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: reporting structured agent-session failures instead of silently discarding them.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is otherwise complete and covers the change, motivation, testing, compatibility, scope, and checklist items. However, the required Linked Issue section states that no tracking issue exists.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Warning

Git: CodeRabbit could not clone the repository, so clone-backed analysis was skipped and this review may be incomplete. Verify repository clone access, such as SSH credentials, before requesting another full review. If clone access is intentionally unavailable, use path_filters to narrow the review scope.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot 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.

ℹ️ No critical issues — one refactor nit inline, one scope note below.

Reviewed changes

  • Structured-session failures now reach the trace sink: ensureStructuredAgentSessionHost in orca-runtime-get-worktree-ps.ts passes an onError that records a failed agent-session.error span with the failure scope as an attribute.
  • Console fallbacks for omitted reporters: every deps.onError call site in structured-agent-session-runtime.ts now falls back to console.error when no reporter is installed, and wraps the reporter call so a throwing reporter cannot poison later callbacks — this also closes a latent unhandled rejection on the late-settlement path.
  • Deps type extracted: StructuredAgentSessionRuntimeDeps moved to structured-agent-session-runtime-deps.ts (re-exported, fields preserved verbatim) to keep the runtime under its line cap.
  • Tests: a wiring test invokes the installer's real onError and reads the actual main.trace.ndjson through createLocalFileSink, and a runtime-errors test covers omitted/configured/throwing reporters across the exit, late-settlement, and journal paths. Both would fail if the wiring or fallbacks regressed.

ℹ️ Failures stay silent on hosts where the tracer sink is inactive

Because the production installer now always provides onError, the runtime's new console.error fallback can never fire on the production path — the if (deps.onError) branch always wins. Where observability is off (orcad, which the PR discloses does not call initObservability; also ORCA_DIAGNOSTICS_DISABLED / CI), startSpan returns its no-op span, so the failure is discarded with neither a trace nor a console line. This is not a regression, but the "no silent discard" guarantee still does not hold on the headless host, and the fallback added here only protects non-production callers (tests).

Technical details
# Headless path has no failure visibility

## Affected sites
- `src/main/runtime/orca-runtime-get-worktree-ps.ts:153-157` — `onError` always defined; writes only to the active tracer sink.
- `src/main/observability/tracer.ts:183-185` — `startSpan` returns `noopSpan` when `activeSink` is null.
- `src/main/orcad/orcad-entry.ts:205` — constructs the same `OrcaRuntimeService`, so it reaches this installer without a sink.

## Required outcome
- Confirm the disclosed follow-up (wire `initObservability` into `orcad`) is tracked.
- Optionally decide whether the production `onError` should also log as a last resort when no sink is installed, so headless failures are not fully invisible.

## Open questions for the human
- Is the `orcad` observability gap tracked by an issue, or should one be filed from this PR?

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread src/main/runtime/structured-agent-session-runtime.ts Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 1adba5ed-f5cf-48ef-9aa6-d0caf1416f18

📥 Commits

Reviewing files that changed from the base of the PR and between f8fbb51 and 5fdde00.

📒 Files selected for processing (2)
  • src/main/runtime/structured-agent-session-runtime-errors.test.ts
  • src/main/runtime/structured-agent-session-runtime.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Comment on lines +184 to +185
} catch (reportingError) {
console.error(`[structured-agent-session] ${label} error reporting failed`, reportingError)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the original failure when onError throws.

This catch path logs only reportingError. If onError throws before it records the runtime error, the original exit, settlement, reaper, or journal failure is still discarded.

Log the original error with its session context. Then log the reporter failure. Update the throwing-reporter test accordingly.

Proposed fix
   } catch (reportingError) {
+    console.error(
+      `[structured-agent-session] ${label} failed${sessionId ? `:${sessionId}` : ''}`,
+      error
+    )
     console.error(`[structured-agent-session] ${label} error reporting failed`, reportingError)
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (reportingError) {
console.error(`[structured-agent-session] ${label} error reporting failed`, reportingError)
} catch (reportingError) {
console.error(
`[structured-agent-session] ${label} failed${sessionId ? `:${sessionId}` : ''}`,
error
)
console.error(`[structured-agent-session] ${label} error reporting failed`, reportingError)

@pullfrog pullfrog Bot 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.

✅ No new issues found.

Reviewed changes

  • Consolidated the five error-reporting blocks: structured-agent-session-runtime.ts:168-187 now routes the orphan reaper, both exit callbacks, late settlement, and journal failures through a single reportRuntimeError(deps, scope, label, error, sessionId?) helper, replacing the five near-identical inline onError-or-console.error blocks. The extracted helper preserves the exact console strings and try/catch behavior at every site, and resolves the prior review's duplication nit.
  • Split late-settlement coverage by provider: structured-agent-session-runtime-errors.test.ts now exercises codex and claude late settlement as separate cases (previously one claude-only case), alongside the exit and journal paths. The codex provider identity shape matches AgentJournalItemIdentity.
  • Merged latest origin/main: the two merge commits carry no authored delta; the deps type extraction and the startSpan('agent-session.error', …).fail(…) wiring are unchanged from the prior review.

Ran the two changed tests locally at this head: 21 passed.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status for head 5fdde00: no proven in-scope defect remains. The conflict with main was resolved without rewriting history; the shared late-settlement callback still covers both Codex and Claude, and the PR remains a focused host-side error-reporting change. GitHub reports CLEAN/MERGEABLE, and all required checks are green after a single surface-lifetime test timed out on the first CI attempt and passed on rerun.

Functional correctness: the production installer now supplies the previously missing error reporter, and exit-recovery, late-settlement, and journal failures reach the bounded, redacted local trace sink; a failing reporter cannot poison the callback. Architecture: error delivery stays with the execution host, with no new session-state copy, retry latch, or steady-state polling. The optional reporter and desktop process-global sink do not cover the headless daemon, which is an explicit separate host-sink/lifecycle gap.

Validation: independent review and start/end readiness passes found zero proven P0/P1/P2 findings; 50 focused tests, typecheck, and targeted lint passed locally. Full CI, including Windows packaging and the retried test shard, is green. Hidden exact-head Electron QA verified app identity, the rendered Privacy diagnostics surface, and an active private NDJSON sink; its uncropped final-head screenshot is in Visual Proof. No real structured-session failure was induced in that app run, so the screenshot is related-surface evidence rather than proof of an agent-session.error span. The existing real-file wiring test supplies that callback-to-trace evidence.

This PR improves diagnosis; it does not establish or fix the cause of the originally reported readable-but-unwritable chat. Live SSH/mobile and daemon error-path validation were not performed for this no-wire/no-mobile delta. I would treat the desktop observability scope as reviewed, with the headless tracing gap tracked separately rather than claiming universal failure coverage.

…ctured-onerror-wiring

# Conflicts:
#	src/main/runtime/structured-agent-session-runtime.ts
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Merge conflict with the current origin/main was resolved in b1bb2d061b and pushed without rewriting history. GitHub now reports the PR as mergeable; fresh CI is queued/running. The resolution preserves main’s shared Codex/Claude late-settlement callback type and this PR’s guarded runtime error reporting. Exact-head validation passed 61 tests across four focused files; typecheck and targeted lint also passed, and the final diff is whitespace-clean.

Architecture status: the failure-reporting path is owned by the execution host and routes runtime faults into the host diagnostic sink without adding reader-side state, retry latches, polling, or steady-state work. It is not full lifecycle alignment: the reporter remains optional and backed by a desktop process-global sink, orcad still does not install that sink, and the existing late-settlement promise is fire-and-forget rather than owned and drained during teardown. Those gaps predate this PR; this change makes their failures observable but does not remove the underlying lifecycle limitations.

I kept the conflict resolution focused on observability rather than expanding this PR into daemon logging or settlement-lifecycle ownership. The only runtime cost is a diagnostic write on an error path.

@pullfrog pullfrog Bot 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.

✅ No new issues found.

Reviewed changes

Re-reviewed the delta since pullfrog's prior review (5fdde00 → b1bb2d0): a merge of origin/main plus two authored adaptations.

  • Late-settlement parameter retyped: onDispatchSettledLate in structured-agent-session-runtime.ts:214-216 now derives its parameter from Parameters<StructuredAgentSessionHost['settleLateDispatch']>[0], dropping the duplicated inline shape and the now-unused AgentJournalItemIdentity import. The body only reads sessionId, so behavior is unchanged.
  • Wiring test installs an app environment: orca-runtime-structured-status-sink-wiring.test.ts:38-48 installs a test AppEnvironment in beforeAll, needed because the merged runtime resolves its state directory through getAppEnvironment().getPath('userData'); without it the real installer would throw on a missing environment.

Ran both changed test files at this head: 21 tests pass. The prior review's duplication thread is resolved.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot 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.

✅ No new issues found.

Reviewed changes

Re-reviewed the delta since pullfrog's prior review (9320989 → 05e315d2): a merge of origin/main that had to reconcile main's new Agent Permissions plumbing with this PR's extracted deps type.

  • Permission deps preserved in the extracted type: structured-agent-session-runtime-deps.ts:1,34-36 gains resolveClaudePermissionMode?: () => Promise<PermissionMode> | PermissionMode and resolveCodexPermissionArgs?: () => string[] plus the PermissionMode import — main's two new fields, carried into the file this PR created rather than dropped during the conflict.
  • Runtime forwards them conditionally: structured-agent-session-runtime.ts:232-234,272-274 passes each to the codex/claude adapters only when defined, matching the surrounding optional-field style.
  • Installer keeps both concerns: orca-runtime-get-worktree-ps.ts:148-152 still installs the agent-session.error trace onError, while :165-168 now re-reads the Agent Permissions setting per acquisition after main removed the configured-CLI-args path.

Verified locally at this head: pnpm run tc:node clean, and the two changed test files pass (21 tests). The prior review's duplication thread is resolved.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Superseded by #24079. This PR wired the one missing onError callback; #24079 removes the optional-callback chain that caused the loss (every structured-session catch site now reports straight to the diagnostic trace) and moves the console-only failure logs, which reach nothing in a packaged build, to the same reporter.

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.

1 participant