Repository navigation
fix(patch): a malformed patch is rejected instead of reported as applied - #826
Merged
Ishaan Gangwani (ishaan1124) merged 4 commits intoSep 29, 2026
Conversation
Two shapes of a malformed patch were accepted and reported to the model as applied. An `*** Update File:` section with no `@@` header parsed to zero chunks, because the section loop only starts a chunk on a line beginning `@@` and skipped every other line. `deriveNewContentsFromChunks` then computed zero replacements, so the "new" content was the file's own bytes and the tool printed `Success. Updated the following files:` with an empty diff. The `hunks.length === 0` guard could not see it, because the `*** Update File:` header had already produced one hunk. A hunk line with no leading space or sign was dropped from both `old_lines` and `new_lines`, because the change-line loop handled `" "`, `"-"` and `"+"` with no `else`. That line is usually the only thing distinguishing one block from another, and `seekSequence` then retried with progressively looser trimming, so an edit asked for in one function could land in another. Both are now parse errors, which the existing `MaybeApplyPatch.PatchParseError` path already reports as a tool error. Two shapes are deliberately left alone: a line that is merely indented is still read as a context line, since no parser can tell ` x` from a lost-prefix ` x`, and a blank line between two hunks still separates them. Fixes synthetic-sciences#775
ANIRUDDHA ADAK (aniruddhaadak80)
requested review from
Aayam Bansal (aayambansal) and
Ishaan Gangwani (ishaan1124)
as code owners
September 29, 2026 05:21
|
ANIRUDDHA ADAK (@aniruddhaadak80) is attempting to deploy a commit to the InkVell Team on Vercel. A member of the Team first needs to authorize it. |
The malformed-hunk guard rejects a change line that appears before any \@@\ header, and a \Move to\ with no hunk below it has no change line, so it passes through with an empty chunk list. That path is valid input per the tool description and has to keep renaming the file.
3 of 8 tasks
…n empty update The malformed-line check also fired on an empty line, so a blank line before `*** End Patch` or between file sections failed a patch that main applies. An empty line inside a hunk is blank context, as in the Codex format; trailing blank lines at the end of a hunk are dropped as separators. An `*** Update File:` with no hunk and no `*** Move to:` still reported success while changing nothing, so it is now rejected; rename-only updates keep working. Co-authored-by: Cursor <cursoragent@cursor.com>
Member
|
I pushed one commit (d9a7f49) on top of yours. The no-prefix check also fired on an empty line, so a blank line before |
# Conflicts: # CHANGELOG.md
Ishaan Gangwani (ishaan1124)
merged commit Sep 29, 2026
afcb78c
into
synthetic-sciences:main
8 of 9 checks passed
6 of 7 tasks
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
apply_patchaccepted two shapes of a malformed patch and reported both to the model as applied. An*** Update File:section with no@@header left the file byte-for-byte unchanged while printingSuccess. Updated the following files:, and a hunk line that lost its leading space or sign was silently dropped, shrinking the context used to place the edit. Both shapes are now parse errors.Root cause
Both defects are in
parseUpdateFileChunksinbackend/cli/src/patch/index.ts.A change line with no
@@header above it was skipped. The section loop only starts a chunk on a line beginning@@and incremented past everything else:So an update section written as
parsed to
chunks: [].deriveNewContentsFromChunksthen computedreplacements = [], so the "new" content was the file's own bytes. Thehunks.length === 0guard in the apply path could not catch it, because the*** Update File:header had already produced one hunk - the tool saw a hunk with nothing in it and reported success with an empty diff.A hunk line with no prefix was dropped from both sides. The change-line loop handled
" ","-"and"+"and had noelse, so a line with none of those prefixes fell out of bothold_linesandnew_lines. That line is typically the only thing distinguishing one block from the next, andseekSequencethen retried with progressively looser passes (rstrip, both-end trim, punctuation folding) to find somewhere to apply the shortened hunk. What the model asked for in one function can land in another.Changes
backend/cli/src/patch/index.tselsethat throws when a line carries no" ","-"or"+"prefix, naming the file and the 1-based line number. The check runs beforei++, so the line is never silently consumed.-or+line appears before any@@header. Lines that are neither a change line nor a header (a blank line between two hunks) still advance normally, so that shape keeps working.parseUpdateFileChunkstakes the file path from its caller so both errors can name the file.parsePatch, which the existingmaybeParseApplyPatchwrapper already turns intoMaybeApplyPatch.PatchParseError, so they surface through the normal tool-error path.backend/cli/test/tool/apply_patch.test.tsrejects an update section that has no hunk header: the first defect. Uses the exact patch from the report and also asserts the file on disk is unchanged.rejects a hunk line that lost its prefix: the second defect. The patch is one that succeeds and applies wrongly today, so it is a real regression rather than a shape that merely reads as malformed; it also asserts the file is unchanged.CHANGELOG.mdUnreleased->Fixed, perAGENTS.md("User-visible behaviour -> a line underUnreleased").Deliberately not changed
Two shapes stay as they are, matching the scope in the report:
xfrom a lost-prefixx, so this one is left alone on purpose.-/+line outside a hunk is rejected, so a section with@@ ... @@and a blank between them parses exactly as before.Testing
Both new tests fail on
f1bdf252with the same assertion, because the tool returns a successful result:With the fix, the whole file is green:
This includes the pre-existing
rejects missing second chunk context, which contains a blank line inside a hunk and still rejects (now with the clearer message), and the twoinsert-only hunkstests, which exercise hunk shapes this change touches.Typecheck:
The one failure is
frontend/workspace/src/custom-elements.d.ts(1,1), which is pre-existing onmainand unrelated to this change - the committed file is 33 bytes containing the bare string../../ui/src/custom-elements.d.ts, which is not a valid declaration file.git statusshows it unmodified againstHEAD, so the breakage is in the commit, not in this working tree. The backend package this PR changes typechecks clean.The repo's
gitleakspre-commit hook ran on the commit and reportedno leaks found.Fixes #775