You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An observed headless execution using CLI 0.4.3 and Gemini 2.5 Flash completed one file-read tool call, then emitted a terminal message with finish: "error". No final text was recorded; there was no session-error event, and the process exited 0. A downstream consumer classified the result as incomplete despite the successful process exit.
The pinned Google SDK 2.0.54 maps only MALFORMED_FUNCTION_CALL to normalized error. This is strong evidence for a malformed generated function call, but the raw response and exact invalid call were not retained. Do not describe the exact offending call or its cause as proven.
Problem / Goal
Provider finish reasons are values, not necessarily exceptions. processor.ts stores an error finish reason without setting a message error; prompt.ts exits for any finish outside tool-calls/unknown; headless error propagation therefore never runs. A completed zero-finding or zero-text turn must remain distinguishable from an unsuccessful model turn.
Proposed Approach
Handle normalized provider error finishes through the existing session/invocation failure lifecycle. Define a shared classification of terminal reasons so event status and process status cannot disagree. Start with explicit error; document existing limit/filter/unknown semantics instead of indiscriminately failing every non-stop finish. Preserve partial usage and tool activity. This fix must ship independently of richer provider diagnostics or recovery.
Acceptance Criteria
A deterministic provider stream fixture emits reasoning followed by an error finish without throwing; the real headless subprocess exits non-zero.
For that fixture, message_complete, structured session failure, session_complete, and invocation_complete agree on failure, have stable correlation IDs, and are emitted without duplicate terminal events.
Output is flushed before exit; existing cancellation and idle-timeout classifications remain distinct.
Successful stop with no text or findings is not failed solely for being empty; a successful final tool call is also covered.
A documented terminal-reason matrix covers error, stop, tool-calls, length, content-filter, other/unknown, thrown provider errors, and cancellation. Tests assert intended existing or explicitly changed behavior.
Preserve known usage and distinguish unknown usage; failed attempts remain observable.
Child-session errors are attributed to the child; they do not automatically fail a parent that handles them successfully.
Update the versioned event documentation and release notes; add the successful-stream/error-finish case to release regression coverage.
Reproduction
Supply a test provider that returns an error finish as a normal streamed response with no thrown exception, then run aictrl run --format json. Reproduce the v0.4.3 contradiction before asserting the corrected outcome. A live Gemini call is not required for the regression.
Out of Scope
Automatic retries, raw provider response capture, product review-completion semantics, GitHub checks, dashboards, and unrelated error-handling refactors.
Roadmap Alignment
Pillar: EXEC; Q3 2026 pilot-ready executor reliability and code-review quality.
Priority: P0 — confirmed production false-success execution.
CLI v0.4.3: packages/cli/src/session/processor.ts, src/session/prompt.ts, src/cli/cmd/run.ts, and src/cli/cmd/run.invocation.ts (paths relative to packages/cli except the first).
Context
An observed headless execution using CLI 0.4.3 and Gemini 2.5 Flash completed one file-read tool call, then emitted a terminal message with
finish: "error". No final text was recorded; there was no session-error event, and the process exited 0. A downstream consumer classified the result as incomplete despite the successful process exit.The pinned Google SDK 2.0.54 maps only
MALFORMED_FUNCTION_CALLto normalizederror. This is strong evidence for a malformed generated function call, but the raw response and exact invalid call were not retained. Do not describe the exact offending call or its cause as proven.Problem / Goal
Provider finish reasons are values, not necessarily exceptions.
processor.tsstores an error finish reason without setting a message error;prompt.tsexits for any finish outsidetool-calls/unknown; headless error propagation therefore never runs. A completed zero-finding or zero-text turn must remain distinguishable from an unsuccessful model turn.Proposed Approach
Handle normalized provider error finishes through the existing session/invocation failure lifecycle. Define a shared classification of terminal reasons so event status and process status cannot disagree. Start with explicit
error; document existing limit/filter/unknown semantics instead of indiscriminately failing every non-stop finish. Preserve partial usage and tool activity. This fix must ship independently of richer provider diagnostics or recovery.Acceptance Criteria
message_complete, structured session failure,session_complete, andinvocation_completeagree on failure, have stable correlation IDs, and are emitted without duplicate terminal events.stopwith no text or findings is not failed solely for being empty; a successful final tool call is also covered.Reproduction
Supply a test provider that returns an error finish as a normal streamed response with no thrown exception, then run
aictrl run --format json. Reproduce the v0.4.3 contradiction before asserting the corrected outcome. A live Gemini call is not required for the regression.Out of Scope
Automatic retries, raw provider response capture, product review-completion semantics, GitHub checks, dashboards, and unrelated error-handling refactors.
Roadmap Alignment
References
packages/cli/src/session/processor.ts,src/session/prompt.ts,src/cli/cmd/run.ts, andsrc/cli/cmd/run.invocation.ts(paths relative topackages/cliexcept the first).aictrl runexits 0 on auth failure, masking broken CI workflows #70 (exception-driven exit status), NDJSON event schema gaps for downstream observability consumers #63 (event contract), Add NDJSON invocation lifecycle events before session creation #90 (invocation lifecycle), Preserve unknown usage on failed headless turns #93 (unknown usage), Add model stream idle-timeout handling #80 (stream idle timeout).Sequencing
This is the first implementation step; richer diagnostics and recovery are not prerequisites.