Skip to content

A runtime fault keeps the output that ran before it - #448

Open
tobert wants to merge 1 commit into
kaijutsu-async-programsfrom
fix/error-keeps-prior-output
Open

A runtime fault keeps the output that ran before it#448
tobert wants to merge 1 commit into
kaijutsu-async-programsfrom
fix/error-keeps-prior-output

Conversation

@tobert

@tobert tobert commented Sep 12, 2026

Copy link
Copy Markdown
Owner

break, continue, return, and exit already carry the output a block produced before they leave it. A runtime fault propagated as a bare anyhow::Error and dropped that output at every block on the way up. A function that faulted became exit 1 with neither its output nor the real cause, because the pipeline stage folded the error with to_string().

echo left && x=$((1/0))                     # printed only the error; now prints left first
if true; then echo in-if; x=$((1/0)); fi    # lost in-if
f() { echo in-f; x=$((1/0)); }; f           # exit 1, "failed to evaluate assignment"
                                            # now: in-f, then the full cause, exit 1

A block that faults wraps the error in a private carrier holding its accumulated output, merging with any output an inner block already attached. The sites are if and while conditions and bodies, for and case bodies, both sides of && and ||, function bodies, source, and .kai scripts. A command substitution keeps only stderr, because its stdout was captured and never printed. The carrier renders exactly as the error it wraps, so no message text changes.

At the top level, a streaming caller receives the faulting statement's partial output through on_output before the Err. A pipeline stage that faults becomes a failed result holding the carried output and the {:#} cause chain.

BREAKING: KernelError::Execution becomes Execution { error, output } so a non-streaming caller can read the same output. A KernelError::Execution(e) pattern no longer compiles; kaijutsu and kaibo do not match on the variant.

This stacks on #445.

Co-Authored-By: DeepSeek V4 Flash noreply@deepseek.com
Co-Authored-By: Claude Opus 5 noreply@anthropic.com

🤖 Generated with Claude Code

`break`, `continue`, `return`, and `exit` already carry the output a
block produced before they leave it. A runtime fault propagated as a
bare `anyhow::Error` and dropped that output at every block on the way
up: `echo left && x=$((1/0))` printed only the error, an `if` body lost
what it printed, and a function that faulted became exit 1 with neither
its output nor the real cause, since the stage fold used `to_string()`.

A block that faults now wraps the error in a private carrier holding
its accumulated output, merging with any output an inner block already
attached: if and while conditions and bodies, for and case bodies, both
sides of && and ||, function bodies, `source`, and `.kai` scripts. A
command substitution keeps only stderr, because its stdout was
captured, never printed. The carrier renders exactly as the error it
wraps, so no message text changes.

At the top level, a streaming caller receives the faulting statement's
partial output through `on_output` before the `Err`.
`KernelError::Execution` becomes `{ error, output }` so a non-streaming
caller can read the same output; this breaks `Execution(e)` patterns.
A pipeline stage that faults becomes a failed result holding the
carried output and the `{:#}` cause chain.

Co-Authored-By: DeepSeek V4 Flash <noreply@deepseek.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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