Skip to content

fix(embed): use Array.isArray guard for agent data in OpenCode embed#482

Merged
jamiepine merged 3 commits intospacedriveapp:mainfrom
eibrahimov:fix/opencode-embed-agent-array-guard
Mar 24, 2026
Merged

fix(embed): use Array.isArray guard for agent data in OpenCode embed#482
jamiepine merged 3 commits intospacedriveapp:mainfrom
eibrahimov:fix/opencode-embed-agent-array-guard

Conversation

@eibrahimov
Copy link
Copy Markdown
Contributor

Summary

  • Fixes the OpenCode embed crash: TypeError: .filter is not a function on sync.data.agent
  • Fixes the web portal error: Unexpected token '<', "<!DOCTYPE"... is not valid JSON when fetching manifest.json

Root Cause

The embed is built from anomalyco/opencode@114eb4244 (OpenCode v1.2.15) but Spacebot runs against the installed OpenCode binary (tested with v1.2.20 and v1.3.0).

When the OpenCode server restarts (triggering a server.instance.disposed SSE event), the embed re-bootstraps and calls GET /agent. If the server responds with an empty body during startup, the SDK client (client.gen.ts) returns {data: {}} (empty object):

// SDK empty-body handling — returns {} not []
case "json": {
  const text = await response.text()
  data = text ? JSON.parse(text) : {}  // ← empty object for empty body
  break
}

The upstream code guards with x.data ?? [] (nullish coalescing), but ?? only catches null/undefined. An empty object {} is truthy and passes through. Calling .filter() on {} throws because plain objects lack Array methods.

Fix

Patches the embed build script (scripts/build-opencode-embed.sh step 2b) to apply Array.isArray() guards instead of ?? in two locations after git checkout but before the Vite build:

File Before After
local.tsx sync.data.agent.filter(...) (Array.isArray(sync.data.agent) ? sync.data.agent : []).filter(...)
bootstrap.ts setStore("agent", x.data ?? []) setStore("agent", Array.isArray(x.data) ? x.data : [])

Patches use perl -pi -e for macOS + Linux portability and are idempotent (safe to re-run).

OpenCode Versions Tested

Component Version
Embed source (fork) anomalyco/opencode@114eb4244 (v1.2.15-based)
Running binary OpenCode v1.2.20 and v1.3.0 (via Homebrew)

Both versions return a valid JSON array from GET /agent, but during server restarts the response may be empty, triggering the SDK's {} fallback.

Test plan

  • just preflight passes
  • just gate-pr passes (697 tests, all green)
  • Desktop app (Tauri): OpenCode embed loads without crash
  • Web portal: manifest.json serves correctly, embed loads
  • Verified Array.isArray guard in built bundle via grep
  • CI passes

The OpenCode embed (built from anomalyco/opencode@114eb4244, v1.2.15)
crashes with `TypeError: .filter is not a function` when the running
OpenCode binary (v1.3.0) restarts.

Root cause: when the server instance is disposed, the embed re-bootstraps
and calls GET /agent. If the server responds with an empty body during
startup, the SDK client returns {data: {}} (empty object). The upstream
code guards with `x.data ?? []`, but nullish coalescing only catches
null/undefined — an empty object `{}` is truthy and passes through.
Calling `.filter()` on `{}` throws because objects lack Array methods.

Fix: patch the embed build to use `Array.isArray()` guards instead of
`??` in two locations:
- local.tsx: `(Array.isArray(sync.data.agent) ? sync.data.agent : []).filter(...)`
- bootstrap.ts: `setStore("agent", Array.isArray(x.data) ? x.data : [])`

Patches are applied by `scripts/build-opencode-embed.sh` step 2b after
git checkout but before the Vite build, using portable perl -pi -e so
they survive rebuilds and work on both macOS and Linux CI.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 21e71135-5281-4fdd-858a-a4f5e7c934de

📥 Commits

Reviewing files that changed from the base of the PR and between b8a6bdd and 3c3b765.

📒 Files selected for processing (1)
  • scripts/build-opencode-embed.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/build-opencode-embed.sh

Walkthrough

The build script now injects two in-place compatibility patches into the vendored OpenCode embed source before dependency install/build, adding Array.isArray(...) guards when accessing agent data in two embed files and aborting if expected source patterns don't match.

Changes

Cohort / File(s) Summary
Build script
scripts/build-opencode-embed.sh
Adds two search-and-replace patch steps that apply and verify compatibility fixes in vendored embed source; aborts the build if expected patterns are not found.
Vendored OpenCode embed files
src/context/local.tsx, src/context/global-sync/bootstrap.ts
Patch changes: wrap sync.data.agent.filter(...) with Array.isArray(sync.data.agent) ? sync.data.agent.filter(...) : [], and replace x.data ?? [] with Array.isArray(x.data) ? x.data : [] to guard against non-array responses.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: applying Array.isArray guards for agent data in the OpenCode embed, which is the primary fix.
Description check ✅ Passed The description is comprehensive and clearly related to the changeset, explaining the root cause, the fix applied to the build script, affected files, and testing performed.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
scripts/build-opencode-embed.sh (1)

114-125: Consider verifying that patches were actually applied.

The perl substitutions silently succeed even if zero matches are found. If upstream refactors these lines (e.g., renames sync.data.agent or changes the setStore call signature), the patches won't match and the build will proceed without the fix—causing the original crash to resurface at runtime.

🛡️ Suggested patch verification
 perl -pi -e \
   's/sync\.data\.agent\.filter\(/(Array.isArray(sync.data.agent) ? sync.data.agent : []).filter(/g' \
   "${LOCAL_TSX}"
-echo "[opencode-embed] Patched local.tsx: Array.isArray guard on agent filter"
+if grep -q 'Array\.isArray(sync\.data\.agent)' "${LOCAL_TSX}"; then
+  echo "[opencode-embed] Patched local.tsx: Array.isArray guard on agent filter"
+else
+  echo "[opencode-embed] ERROR: local.tsx patch did not apply — upstream may have changed."
+  exit 1
+fi

 perl -pi -e \
   's/input\.setStore\("agent",\s*x\.data\s*\?\?\s*\[\]\)/input.setStore("agent", Array.isArray(x.data) ? x.data : [])/g' \
   "${BOOTSTRAP_TS}"
-echo "[opencode-embed] Patched bootstrap.ts: Array.isArray guard on agent store"
+if grep -q 'Array\.isArray(x\.data)' "${BOOTSTRAP_TS}"; then
+  echo "[opencode-embed] Patched bootstrap.ts: Array.isArray guard on agent store"
+else
+  echo "[opencode-embed] ERROR: bootstrap.ts patch did not apply — upstream may have changed."
+  exit 1
+fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build-opencode-embed.sh` around lines 114 - 125, The current perl
substitutions for the patterns containing "sync.data.agent.filter" and
'input.setStore("agent", x.data ?? [])' may be no-ops if upstream changed those
lines; after each patch command (targeting sync.data.agent.filter and
input.setStore("agent", x.data ?? [])), add a verification step that checks the
file for the new guarded form (Array.isArray(...)) and fail the script if the
expected replacement wasn't applied so the CI/build stops (e.g., grep for
"Array.isArray(sync.data.agent" and 'input.setStore("agent",
Array.isArray(x.data)' or count replacements and exit non-zero when zero),
ensuring you reference the exact symbols "sync.data.agent.filter" and
'input.setStore("agent", x.data ?? [])' to locate the original lines and the
guarded forms to verify.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/build-opencode-embed.sh`:
- Around line 114-125: The current perl substitutions for the patterns
containing "sync.data.agent.filter" and 'input.setStore("agent", x.data ?? [])'
may be no-ops if upstream changed those lines; after each patch command
(targeting sync.data.agent.filter and input.setStore("agent", x.data ?? [])),
add a verification step that checks the file for the new guarded form
(Array.isArray(...)) and fail the script if the expected replacement wasn't
applied so the CI/build stops (e.g., grep for "Array.isArray(sync.data.agent"
and 'input.setStore("agent", Array.isArray(x.data)' or count replacements and
exit non-zero when zero), ensuring you reference the exact symbols
"sync.data.agent.filter" and 'input.setStore("agent", x.data ?? [])' to locate
the original lines and the guarded forms to verify.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: acf2a670-5b40-4698-9dd5-c95a711dd2f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6f81f39 and b8a6bdd.

📒 Files selected for processing (1)
  • scripts/build-opencode-embed.sh

Address CodeRabbit review: perl substitutions silently succeed on
zero matches. Add grep verification after each patch to fail the
build early if upstream refactors the target lines.
@jamiepine jamiepine merged commit 14e1676 into spacedriveapp:main Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants