Repository navigation
fix(wasm): report WABT output past its budget as a resource constraint - #2117
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
The red checks come from |
|
@greptileai review |
|
A module whose WAT exceeds the 32 MiB output budget failed as unreadable_output, advised `rea doctor`, and echoed the full 32 MiB truncated WAT prefix in details.captured_output. Map the owned-command output-limit outcome during inspection to AnalysisResourceConstraintError naming the budget, as the apktool and native macOS providers do, with a remediation that says the fixed budget will not change on retry. Captured output for an output-limit failure keeps only the first 4,096 characters of each stream, including on the cleanup-failure path. An oversized --version probe keeps its configuration-phase mapping.
…excerpts Cut each stream's output-limit excerpt with boundedUtf8Text at 4,096 UTF-8 bytes, so an astral character at the cut no longer leaves a lone surrogate. Mark the output truncated when either excerpt was cut, and report each stream's observed byte count. Word the remediation as a fact about the fixed budget. A flood of diagnostics from any WABT tool reaches this path, so it no longer says the module is too large. Replace the 32 MiB end-to-end test with a launcher seam that raises a small output-limit failure.
Report retained byte counts in stdout_bytes and stderr_bytes, as apktool and the firmware command do, and record exit_code and signal. Mark the excerpt truncated outright, since the command raises output-limit only after retention was truncated. When cleanup also fails, record the output limit and its budget as the previous error instead of the generic command message. Document that the two streams share the budget, so the excerpt holds up to 4,096 of the bytes each stream retained.
922a1da to
463a917
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 463a9176de
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 913cd9c7a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
|
Review findings addressed at their producing boundaries and consumers. Latest changes were reviewed from source; tests and provider runs were skipped at the maintainer's request. Findings requiring separate decisions or verification remain open. The guide now distinguishes the contributor-reported warm Linux x64 WABT 1.0.42 CLI elapsed/CPU/process-family-memory measurements from the unmeasured token usage and truly cold workflows. It links the PR evidence and explicitly retains the full underlying cause snapshot, so excerpting establishes no memory or speed improvement. Current source: c5beb60. Validation: source inspection and the existing PR evidence only; tests/checks were skipped at the user's explicit request. The note now attributes the existing warm Linux x64 WABT 1.0.42 elapsed/CPU/process-family-memory measurements to PR #2117, and limits the unmeasured gaps to agent tokens and truly cold runs. It preserves the full-snapshot caveat and makes no unsupported memory or speed improvement claim. Current source: c5beb60. Validation: source inspection only; tests and checks were skipped at the user's explicit request. |
|
Thanks for tightening the guide. I ran the checks you skipped against c5beb60: the |
|
Thanks for merging, and for tightening the guide's wording on the measurements. |
Summary
inspect-wasm-artifactreports the wrong kind of failure when a module's WABT output is larger than the 32 MiB output budget. It returnsunreadable_output, tells the caller to runrea doctor, and puts the first 33,554,432 bytes of WAT intodetails.captured_output.stdout, so the error is 33.6 MB of JSON. No issue is linked. I found this by running the tool over the real.wasmfiles on one machine, and the fix is small enough to explain here.Problem and expected behavior
runOwnedCommandstops a WABT command when its output passesWABT_LIMITS.outputBytes(32 MiB) and throwsOwnedCommandFailurewith reasonoutput-limit.wabtFailureinsrc/wasm/WabtFailures.tshas branches forcancelled,timeoutandprocess, and none foroutput-limit. The failure falls through toAnalysisOutputError, which the CLI shows as an unreadable result, with all of the retained stdout attached.The budget works as documented. The caller is told the result was unreadable when that documented limit was hit, and gets back the partial WAT that the limit exists to refuse.
3 of the 28 valid modules I ran hit this. All three are tree-sitter grammars. Sizes from WABT 1.0.42 run directly:
wasm2wat --enable-allbyteswasm-objdump -h -xbytestree-sitter-cpp.wasm(VS Code Copilot)tree-sitter-bash.wasm(@vscode/tree-sitter-wasm)tree-sitter-powershell.wasm(@vscode/tree-sitter-wasm)Expected: an error that says which limit was hit and does not carry 32 MiB of partial output. The apktool provider (
src/apktool/ApktoolProvider.ts) and the native macOS provider (src/native/NativeMacOSProvider.ts) already mapoutput-limittoAnalysisResourceConstraintErrorwith resourcememoryandmax_output_bytes.Change and scope
wabtFailuremapsoutput-limittoAnalysisResourceConstraintError, as apktool and native macOS do. The message gives the budget in bytes, andreported_limitscarriesmax_output_bytes.memoryconstraint says to retry with more memory, and no memory setting changes this budget. The text does not say the module is too large, because any of the three tools can fill the budget, includingwasm-validateprinting diagnostics for a malformed module.output-limitfailure,captured_outputkeeps up to the first 4,096 bytes that stdout and stderr each retained. The two streams share one budget, so either can be the one that filled it, and a stream that starts after the other has filled it retains nothing. The cut uses the existingboundedUtf8Texthelper, so it never splits a character.truncatedis always true here, because the command raisesoutput-limitonly after it truncated what it retained. It also recordsstdout_bytes,stderr_bytes,exit_codeandsignal, as apktool does. The byte counts are retained bytes, the meaning apktool and the firmware command give them.cleanup_incomplete. Its diagnostics now record the output limit and the budget asprevious_error, and carry the same bounded excerpt.capturedEvmOutputandcapturedPwntoolsOutputalready build captured output from a snapshot. They copy the full text and have no excerpt step, and importing them would make WABT depend on EVM or pwntools code, so I did not reuse them.--versionprobe in the configuration phase keeps its current mapping. A misconfigured tool should not be reported as an oversized module.wasm-validate,wasm-objdumpandwasm2watall run through the sameexecutehelper, so all three reach this mapping. REA does not runwasm-decompileor any other WABT tool.docs/wasm-artifacts.mdsaid "truncation fails". It now gives the error code and the excerpt size.Not changed, and yours to decide:
memory, as in apktool and native macOS for the same output budget. No sibling uses a closer value, so I kept it for consistency.WabtArtifactProvider.ts(around line 385) still attach full captured output. Only the output-limit path is bounded here.causestill holds the full snapshot in memory. It is not serialized.output-limittoAnalysisOutputError, so they show the samerea doctoradvice for a size limit. Apktool keeps its capture unbounded. One excerpt step at the owning boundary insrc/process/could cover all of them. That touches more providers, so I left it out of this PR.stdout_bytesandstderr_byteskeep their shared meaning of retained bytes. The snapshot also knows how many bytes each stream produced before it stopped. That could go into the message or a new field if you want it.execute()throws before it re-checks the executable digest and the module snapshot. So a swapped binary that floods output is reported as over budget, and its changed digest goes unnoticed. That control flow predates this change.--versionprobe still maps tounreadable_output.capability_unavailablemay fit a wrong tool better, as it does for a wrong banner.src/wasm/WabtFailures.tsand its test file. The two branches merge without conflicts.Contract and boundary impact
wabtFailure(src/wasm/WabtFailures.ts).inspect_wasm_artifactreturns the existingresource_constraintcode instead ofunreadable_output. No schemas change.captured_outputkeeps up to 4,096 bytes of what each stream retained, the retained byte counts,exit_code,signalandtruncated.Evidence and regression coverage
src/wasm/WabtArtifactProvider.test.ts:inspect, the real child processes run for the version probes andwasm-objdump, and the launcher raises an output-limit failure forwasm2wat. The test checks the error tag, resource,max_output_bytes, the projectedresource_constraintcode, that the message names the budget, the remediation, and the fullcaptured_outputwith the excerpt, byte counts, exit code and signal. No test writes 32 MiB.previous_error.--versionprobe staysAnalysisOutputError.mainthe first four tests fail. The fifth passes there, since it guards the configuration-phase behaviour this change keeps. Each part of the fix is needed. Keeping the full text fails the four excerpt tests. Cutting by UTF-16 units fails the emoji test. Settingtruncatedto false, reporting observed instead of retained byte counts, or droppingexit_codeandsignaleach fails theinspectand stderr tests. Recording the generic command message asprevious_errorfails the cleanup test. Dropping the phase condition fails the--versiontest. Changing the resource tocpu, droppingmax_output_bytes, dropping the byte count from the message, or using the old module-size remediation each fails theinspecttest.tree-sitter-cpp.wasm(stdout excerpt shortened here):{"code":"resource_constraint","category":"resource_constraint","message":"WABT output exceeded the 33554432-byte output budget","retryable":false,"remediation":{"action":"The WABT output budget is fixed, so retrying with the same input will fail the same way."},"details":{"operation":"inspect_wasm_artifact","resource":"memory","reason":"WABT output exceeded the 33554432-byte output budget","reported_limits":{"max_output_bytes":33554432},"captured_output":{"stdout":"(module\n (type (;0;) (func (param i32))...","stderr":"","truncated":true,"stdout_bytes":33554432,"stderr_bytes":0,"exit_code":0,"signal":null}}}For evidence-bearing changes:
Validation performed
vitestoversrc/wasm,tests/boundary/mcp/wasmArtifactMcp.test.ts,tests/acceptance/tools/localToolContracts.test.ts,src/contracts/toolEffects.test.tsandtests/boundary/filesystem/targetDiagnostics.test.ts: 6 files, 56 tests passed.npm run check:fast: passed.npm run docs:check: passed.npm run verify:test-discovery: "Discovered all 915 test files exactly once."stdout_bytes33,554,432.exit_codeandsignaldepend on whether WABT finished before it was stopped: the same module gave exit 0 on one run andSIGKILLon another.tree-sitter-cpp.wasmunreadable_output, 33,606,368-byte errorresource_constraint, 4,932-byte errortree-sitter-bash.wasmunreadable_output, 33,584,062-byte errorresource_constraint, 4,931-byte errortree-sitter-powershell.wasmunreadable_output, 33,568,405-byte errorresource_constraint, 4,926-byte errortree-sitter-c-sharp.wasm,onig.wasm,crc32.wasmvsda_bg.wasm(not WebAssembly)invalid_requestinvalid_requestCosts, measured 2026-10-13 with the built CLI on this branch and on
mainat 4276aaf, WABT 1.0.42, Linux x64, 12 cores. Each run had its own cgroup, so CPU and peak memory cover the whole process family, WABT children included. Per module and tree: one first run and three warm runs, withmainand the branch alternating. The machine was shared, with a load average of 8 to 14 during the runs, so differences of a few hundred milliseconds are noise. "First" is not truly cold: the page cache was already warm from the build, and I could not drop it.tree-sitter-cpp.wasmmaintree-sitter-bash.wasmmaintree-sitter-powershell.wasmmaintree-sitter-c-sharp.wasm(control, succeeds)mainElapsed time, CPU time and peak memory show no change outside that noise. The process still reads the full 32 MiB before stopping, and
causestill holds the snapshot, so I did not expect one. The change is in what the caller receives: about 4.9 KB instead of about 33.6 MB per failure. Token usage was not measured. I had no tokenizer for the agent's model on this machine, and fewer bytes alone do not prove fewer tokens.Compatibility, safety, and release
unreadable_outputfor oversized modules now getsresource_constraint. Neither code is retryable.Review checklist
type(scope): outcome.