Description
When apply_patch processes a delete operation on a large or binary file (e.g. a 142 MB .dmg), it calls createTwoFilesPatch(filePath, filePath, entireFileContent, "") to build a unified diff. For a 142 MB binary file this produces a diff string of ~380 MB. That string is stored verbatim in the SQLite part.data column.
On the next query that runs StatementSync.all() over that session's parts, Node's built-in sqlite module passes the 380 MB char* to v8::String::NewFromUtf8. V8 has an internal string-length assertion (~512 MB limit) and hits it as a SIGTRAP / EXC_BREAKPOINT, bringing the entire sidecar process down.
The same risk exists in edit.ts's trimDiff function, which is called from all edit/patch paths and does no size check before splitting the diff on newlines.
Crash stack from macOS crash report:
v8::String::NewFromUtf8
node::sqlite::StatementExecutionHelper::ColumnToValue
node::sqlite::ExtractRowValues
node::sqlite::StatementExecutionHelper::All
node::sqlite::StatementSync::All
Steps to reproduce
- Start opencode on macOS.
- Ask the AI to delete a binary file >= ~50 MB (e.g. a
.dmg, .iso, or large .zip) via apply_patch.
- The sidecar process crashes with SIGTRAP / EXC_BREAKPOINT immediately after the patch is applied. All in-flight sessions are lost.
OS
macOS 15 (Darwin arm64)
OpenCode version
Latest dev branch (reproduced on current dev HEAD)
Root cause
apply_patch.ts (delete case): no size guard before calling createTwoFilesPatch + storing the result in SQLite.
edit.ts (trimDiff): no early return for strings that already exceed a safe size threshold, so even if the caller skipped the DB write, downstream processing would still allocate a multi-hundred-MB string.
Description
When
apply_patchprocesses adeleteoperation on a large or binary file (e.g. a 142 MB.dmg), it callscreateTwoFilesPatch(filePath, filePath, entireFileContent, "")to build a unified diff. For a 142 MB binary file this produces a diff string of ~380 MB. That string is stored verbatim in the SQLitepart.datacolumn.On the next query that runs
StatementSync.all()over that session's parts, Node's built-insqlitemodule passes the 380 MBchar*tov8::String::NewFromUtf8. V8 has an internal string-length assertion (~512 MB limit) and hits it as aSIGTRAP/EXC_BREAKPOINT, bringing the entire sidecar process down.The same risk exists in
edit.ts'strimDifffunction, which is called from all edit/patch paths and does no size check before splitting the diff on newlines.Crash stack from macOS crash report:
Steps to reproduce
.dmg,.iso, or large.zip) viaapply_patch.OS
macOS 15 (Darwin arm64)
OpenCode version
Latest dev branch (reproduced on current
devHEAD)Root cause
apply_patch.ts(delete case): no size guard before callingcreateTwoFilesPatch+ storing the result in SQLite.edit.ts(trimDiff): no early return for strings that already exceed a safe size threshold, so even if the caller skipped the DB write, downstream processing would still allocate a multi-hundred-MB string.