Summary
Automated audit found error-handling gaps and boundary-condition bugs across packages/app/src/ and packages/opencode/src/.
Functions returning undefined without callers checking
| File |
Line |
Issue |
packages/opencode/src/git/index.ts |
190-192 |
primary() returns undefined if list is empty; callers may treat as guaranteed string |
packages/opencode/src/session/prompt.ts |
1317 |
lastModel() can return undefined; next line accesses model.providerID unconditionally |
packages/opencode/src/session/prompt.ts |
2175 |
taskModel resolved via lastModel() fallback; line 2178 accesses providerID/modelID without check |
Divide by zero / NaN propagation
| File |
Line |
Issue |
packages/opencode/src/tool/read.ts |
152 |
nonPrintableCount / bytes.length — no guard for empty file (0 bytes), yields NaN |
packages/opencode/src/session/prompt.ts |
1470 |
parseInt(range.start) with no radix and no isNaN check — NaN flows into offset/limit |
String operations on potentially undefined
| File |
Line |
Issue |
packages/opencode/src/session/prompt.ts |
1469 |
part.url.split("?")[0] — part.url not validated before .split() |
packages/opencode/src/session/prompt.ts |
1603 |
filepath interpolated into pseudo-JSON without quote escaping |
packages/opencode/src/id/id.ts |
81 |
id.split("_")[0] — id may not contain _ |
Missing try-catch / .catch() on async operations
| File |
Line |
Issue |
packages/app/src/components/terminal.tsx |
433 |
document.fonts.ready.then(scheduleFit) — no .catch() |
packages/app/src/pages/layout.tsx |
1527, 1535, 1755 |
Dynamic imports without .catch() — unhandled rejection if chunk fails |
packages/app/src/context/global-sync.tsx |
135 |
void projectInit.then(...) — no .catch() |
packages/opencode/src/session/turn-change.ts |
1016 |
previous.then(() => current) inside locked — rejection unhandled until await |
Missing switch defaults
| File |
Line |
Issue |
packages/opencode/src/tool/agent.ts |
138 |
switch (part.status) — no default; new status → header/body remain undefined |
packages/opencode/src/acp/agent.ts |
1379 |
switch (part.type) — no default; unknown types silently dropped |
packages/opencode/src/session/export.ts |
793 |
switch (part.state.status) — no default; returns undefined |
packages/opencode/src/tool/apply_patch.ts |
288 |
switch (change.type) — no default; unknown changes silently skipped |
Effect.orDie / Effect.catch(Effect.die) — process crashes
~15 places where routine I/O or permission failures become fatal defects:
| File |
Line |
Issue |
packages/opencode/src/session/prompt.ts |
573 |
fsys.ensureDir(...).pipe(Effect.catch(Effect.die)) |
packages/opencode/src/session/prompt.ts |
733 |
permission.ask(...).pipe(Effect.orDie) |
packages/opencode/src/session/prompt.ts |
1612 |
fsys.readFile(...).pipe(Effect.catch(Effect.die)) |
packages/opencode/src/session/prompt.ts |
1968 |
instruction.system().pipe(Effect.orDie) |
packages/opencode/src/tool/write.ts |
127 |
run(params, ctx).pipe(Effect.orDie) |
packages/opencode/src/tool/enter-worktree.ts |
195 |
run(params, ctx).pipe(Effect.orDie) |
packages/opencode/src/tool/exit-worktree.ts |
76 |
run(params, ctx).pipe(Effect.orDie) |
packages/opencode/src/tool/question.ts |
55 |
run(params, ctx).pipe(Effect.orDie) |
packages/opencode/src/tool/skill.ts |
72 |
run(params, ctx).pipe(Effect.orDie) |
packages/opencode/src/tool/agent-list.ts |
72 |
run(params, ctx).pipe(Effect.orDie) |
packages/opencode/src/tool/truncate.ts |
71-72 |
fs.ensureDir(...).pipe(Effect.orDie) and fs.writeFileString(...).pipe(Effect.orDie) |
User input without sanitization
| File |
Line |
Issue |
packages/opencode/src/cli/cmd/providers.ts |
300 |
fetch(${url}/.well-known/opencode) — no URL validation (SSRF risk) |
packages/opencode/src/cli/cmd/db.ts |
33 |
db.query(query).all() — arbitrary SQL from CLI args |
packages/opencode/src/config/paths.ts |
119 |
{file:...} token resolves outside config dir (path traversal) |
packages/opencode/src/session/prompt.ts |
2158 |
Shell commands from template without additional validation |
Summary
Automated audit found error-handling gaps and boundary-condition bugs across
packages/app/src/andpackages/opencode/src/.Functions returning
undefinedwithout callers checkingpackages/opencode/src/git/index.tsprimary()returnsundefinediflistis empty; callers may treat as guaranteed stringpackages/opencode/src/session/prompt.tslastModel()can returnundefined; next line accessesmodel.providerIDunconditionallypackages/opencode/src/session/prompt.tstaskModelresolved vialastModel()fallback; line 2178 accessesproviderID/modelIDwithout checkDivide by zero / NaN propagation
packages/opencode/src/tool/read.tsnonPrintableCount / bytes.length— no guard for empty file (0 bytes), yieldsNaNpackages/opencode/src/session/prompt.tsparseInt(range.start)with no radix and noisNaNcheck —NaNflows intooffset/limitString operations on potentially undefined
packages/opencode/src/session/prompt.tspart.url.split("?")[0]—part.urlnot validated before.split()packages/opencode/src/session/prompt.tsfilepathinterpolated into pseudo-JSON without quote escapingpackages/opencode/src/id/id.tsid.split("_")[0]—idmay not contain_Missing try-catch /
.catch()on async operationspackages/app/src/components/terminal.tsxdocument.fonts.ready.then(scheduleFit)— no.catch()packages/app/src/pages/layout.tsx.catch()— unhandled rejection if chunk failspackages/app/src/context/global-sync.tsxvoid projectInit.then(...)— no.catch()packages/opencode/src/session/turn-change.tsprevious.then(() => current)insidelocked— rejection unhandled until awaitMissing switch defaults
packages/opencode/src/tool/agent.tsswitch (part.status)— no default; new status →header/bodyremainundefinedpackages/opencode/src/acp/agent.tsswitch (part.type)— no default; unknown types silently droppedpackages/opencode/src/session/export.tsswitch (part.state.status)— no default; returnsundefinedpackages/opencode/src/tool/apply_patch.tsswitch (change.type)— no default; unknown changes silently skippedEffect.orDie/Effect.catch(Effect.die)— process crashes~15 places where routine I/O or permission failures become fatal defects:
packages/opencode/src/session/prompt.tsfsys.ensureDir(...).pipe(Effect.catch(Effect.die))packages/opencode/src/session/prompt.tspermission.ask(...).pipe(Effect.orDie)packages/opencode/src/session/prompt.tsfsys.readFile(...).pipe(Effect.catch(Effect.die))packages/opencode/src/session/prompt.tsinstruction.system().pipe(Effect.orDie)packages/opencode/src/tool/write.tsrun(params, ctx).pipe(Effect.orDie)packages/opencode/src/tool/enter-worktree.tsrun(params, ctx).pipe(Effect.orDie)packages/opencode/src/tool/exit-worktree.tsrun(params, ctx).pipe(Effect.orDie)packages/opencode/src/tool/question.tsrun(params, ctx).pipe(Effect.orDie)packages/opencode/src/tool/skill.tsrun(params, ctx).pipe(Effect.orDie)packages/opencode/src/tool/agent-list.tsrun(params, ctx).pipe(Effect.orDie)packages/opencode/src/tool/truncate.tsfs.ensureDir(...).pipe(Effect.orDie)andfs.writeFileString(...).pipe(Effect.orDie)User input without sanitization
packages/opencode/src/cli/cmd/providers.tsfetch(${url}/.well-known/opencode)— no URL validation (SSRF risk)packages/opencode/src/cli/cmd/db.tsdb.query(query).all()— arbitrary SQL from CLI argspackages/opencode/src/config/paths.ts{file:...}token resolves outside config dir (path traversal)packages/opencode/src/session/prompt.ts