pr-comment: pipe changes payload via stdin to avoid jq ARG_MAX failure#118
Merged
Conversation
The pr-comment entrypoint built its JSON payload via `jq -n --argjson
changes "$changes"`, which puts the entire JSON string on jq's command
line. For real-world specs the changes array can run into the
multi-megabyte range, exceeding the OS argument-length limit (ARG_MAX,
typically 2 MB on Linux). The script then aborts with:
/entrypoint.sh: line N: jq: Argument list too long
right before the POST to oasdiff-service, so the customer sees:
* The free `::notice::` review URL printed earlier (which is
indistinguishable from what the free `breaking` action would emit)
* No PR comment from oasdiff[bot] (because the service never received
the report)
* No commit-status check transition
...and assumes either the workflow swap from `breaking` to `pr-comment`
didn't take effect, or that approve/reject is broken for them. The
action's failure is silent in the workflow's overall status because the
script aborts under `set -e` after the notice line has already printed.
Fix: pipe `$changes` to jq via stdin and reference it as `.` in the
filter expression. `printf` is a shell builtin in /bin/sh / busybox ash,
so the variable is never on argv and ARG_MAX is irrelevant. The other
`--arg` values are short (token, owner/repo, SHAs, file paths) and stay
on the command line unchanged.
Adds a regression test in .github/workflows/test.yaml that stubs
oasdiff to emit a ~4 MB changelog and asserts the entrypoint reaches
the no-token skip without aborting. If the regression returns (someone
reverts to `--argjson changes`), the synthetic payload will trip
ARG_MAX on the CI runner and the test fails with a precise error
message pointing at the cause.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new pr_comment_handles_large_payload job ran `yes a | head -c 2000000 | tr -d '\n'` to build the filler string. Under the workflow shell's `set -o pipefail`, `yes` exits with SIGPIPE (141) when `head` closes the pipe, which fails the pipeline and aborts the step before the test even runs. Classic pattern: infinite source plus bounded head plus pipefail. Switch to `dd if=/dev/zero bs=1024 count=2000 status=none | tr '\0' 'a'`. dd reads a bounded number of zero bytes and exits 0 cleanly, so the pipeline succeeds under pipefail. Output is identical: a 2 MB string of 'a' characters. Confirmed locally with `set -o pipefail` enabled: the pipeline returns 0 and produces 2 048 000 bytes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
reuvenharrison
added a commit
that referenced
this pull request
May 22, 2026
) Same class of bug as #118 (jq ARG_MAX), one layer down. After #118 fixed the jq invocation, the next line on the failure path is response=$(curl ... -d "$payload") The full JSON payload (~4 MB for the customer spec that surfaced the jq case) goes through curl's argv via `-d`, exceeds the OS argument- length limit (ARG_MAX, typically 2 MB on Linux), and the script aborts with: /entrypoint.sh: line 124: curl: Argument list too long right after the (now-successful) jq step, with the same downstream symptoms: free `::notice::` URL printed, no PR comment posted, no review-token link generated. Customer saw "same problem" after bumping their pin to @main with #118 applied — the jq step now works but the next layer hits the same trap. Fix: pipe the payload to curl via stdin and use `--data-binary @-` to consume from stdin. printf is a shell builtin so the variable never goes through execve. Same shape as the jq fix. Extends the test workflow with `pr_comment_curl_handles_large_payload`, a companion to the existing `pr_comment_handles_large_payload`. The previous test exercises the jq path with an empty oasdiff_token, which short-circuits past the curl step. The new test exercises the curl step itself by providing a non-empty token and stubbing both oasdiff (multi-MB changelog source) and curl (records the body byte count from stdin and emits a fake 200 response). Asserts the curl stub was invoked with >4 MB of body — proves the payload made it through stdin without hitting ARG_MAX on argv. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The pr-comment entrypoint built its JSON payload by passing the diff content as a
jq --argjson changes "$changes"value. For specs whose changelog runs into the multi-megabyte range, the JSON string exceeds the OS argument-length limit (ARG_MAX, typically 2 MB on Linux), andjqaborts with:This happens after the script has printed the free
::notice::review URL but before the POST to oasdiff-service, so the customer sees a/review?...notice in their action log, no PR comment fromoasdiff[bot], and assumes the Pro workflow swap had no effect. Silent failure underset -ebecause the notice line has already been printed and the workflow step exit code propagates without an obvious "this is the cause" pointer.Fix
Pipe
$changesthrough stdin instead.printfis a shell builtin in POSIX/bin/sh/ busyboxash, so the variable is never onargvand ARG_MAX is irrelevant. The jq filter changes$changesto.(the implicit stdin input), and the-n(null-input) flag is dropped since jq now consumes real input.The other
--argvalues (token, owner/repo, SHAs, file paths) are short and stay on the command line unchanged.Regression test
A new job
pr_comment_handles_large_payloadin.github/workflows/test.yamlstubsoasdiffto emit a ~4 MB synthetic changelog (two entries with 2 MB of filler each, well above the 2 MB Linux ARG_MAX ceiling) and asserts the entrypoint reaches the "No oasdiff-token provided" skip without aborting. If someone later reverts to--argjson changes "$changes", the synthetic payload trips ARG_MAX on the runner and the test fails with a specific error message pointing at the cause.Follows the same stub-
oasdiff-on-PATH pattern as the existingpr_comment_tolerates_oasdiff_fail_onandpr_comment_aborts_on_oasdiff_real_failurejobs.Test plan
bash -n pr-comment/entrypoint.shcleanDiscovered by
A Pro customer trying to set up the
pr-commentworkflow on a multi-thousand-endpoint spec. Their action log surfaced the exactjq: Argument list too longerror from line 89; the fix shape was unambiguous from there.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Generated with Claude Code