Skip to content

bug: 07-devtools jq rewrite leaves a previously-corrupt opencode.json broken — sed fallback never runs as recovery #332

@yasinBursali

Description

@yasinBursali

Bug Report: 07-devtools jq rewrite leaves a previously-corrupt opencode.json broken — sed fallback never runs as recovery

Severity: Medium
Category: Installer / Error Handling
Platform: All (Linux, macOS, Windows/WSL2) — confirmed on WSL2
Confidence: Confirmed

Follow-up to: #309 ([WSL2] Installer (07-devtools): reinstall corrupts opencode.json — sed strips trailing comma).
#309 was closed by PR Light-Heart-Labs#901, but the fix has a recovery hole: it prevents NEW corruption but does not recover from EXISTING corruption left behind by the pre-Light-Heart-Labs#901 sed.

Description

PR Light-Heart-Labs#901 fixed the WRITE path in 07-devtools.sh so new installs don't corrupt opencode.json by eating trailing commas. But it doesn't recover from EXISTING corruption: when jq is installed AND the existing opencode.json is already malformed (e.g. left over from a pre-Light-Heart-Labs#901 install), jq's parse fails on the input file and the fix logs ai_warn "OpenCode config jq rewrite failed; leaving existing config in place" and keeps the broken file. The narrow-sed fallback only runs in the else branch (no jq present) — it never runs as recovery for jq parse failure.

Effect: users who upgrade from a pre-Light-Heart-Labs#901 broken state are stuck with a permanently broken OpenCode config. They don't get out of it without manual intervention.

Affected File(s)

  • dream-server/installers/phases/07-devtools.sh:156-174 — the if command -v jq >/dev/null 2>&1 block in the "Reinstall: update API key and URL in existing config" branch.

Root Cause

The "if jq is installed AND jq succeeds" path is two nested ifs, but the fallback to sed lives only in the OUTER else (no jq), not in the INNER else (jq present but failed):

if command -v jq >/dev/null 2>&1; then
    _opencode_tmp="$OPENCODE_CONFIG_DIR/opencode.json.tmp.$$"
    if jq --arg url ... '...' "$OPENCODE_CONFIG_DIR/opencode.json" > "$_opencode_tmp"; then
        mv "$_opencode_tmp" "$OPENCODE_CONFIG_DIR/opencode.json"
        ai_ok "OpenCode config updated (API key and URL refreshed)"
    else
        rm -f "$_opencode_tmp"
        ai_warn "OpenCode config jq rewrite failed; leaving existing config in place"
        # ← BUG: no fall-through to sed fallback. The broken file is preserved.
    fi
else
    # narrow-sed fallback — only runs when jq is ABSENT (not when jq parse fails)
    _sed_i "s|\"apiKey\": *\"[^\"]*\"|\"apiKey\": \"${_opencode_key}\"|" "..."
    _sed_i "s|\"baseURL\": *\"[^\"]*\"|\"baseURL\": \"${_opencode_url}\"|" "..."
    ai_ok "OpenCode config updated (API key and URL refreshed)"
fi

Evidence

In the test environment, the previous install (pre-Light-Heart-Labs#901) had left behind a corrupt ~/.config/opencode/opencode.json with the exact symptom #309 described — baseURL and apiKey siblings without a separator comma between them:

{
  ...
  "options": {
    "baseURL": "http://127.0.0.1:11434/v1"
    "apiKey": "no-key"
  },
  ...
}

After running bash install.sh --all --non-interactive --no-comfyui from a branch with PR Light-Heart-Labs#901 merged, install log line 101:

⚠ OpenCode config jq rewrite failed; leaving existing config in place

The corrupt file is unchanged (mtime is still from the previous install). Manual jq reproduction:

$ jq --arg url "http://test/v1" --arg key "no-key" \
    '.provider["llama-server"].options.baseURL = $url
     | .provider["llama-server"].options.apiKey = $key' \
    ~/.config/opencode/opencode.json
jq: parse error: Expected separator between values at line 11, column 16
$ echo $?
5

jq -Vjq-1.7.

Platform Analysis

  • macOS: Affected. macOS ships with jq via Homebrew on most dev installs. Same fail path.
  • Linux (Ubuntu 24.04 in this test): Affected. jq is in default apt and is installed via the same phase that runs this code.
  • Windows/WSL2: Affected. Confirmed. WSL2 inherits the Linux path.

Reproduction

  1. Have a corrupt ~/.config/opencode/opencode.json (e.g. produced by Light-Heart-Labs/main < fix(07-devtools): preserve trailing comma when rewriting opencode.json Light-Heart-Labs/DreamServer#901 — the original sed bug from [WSL2] Installer (07-devtools): reinstall corrupts opencode.json — sed strips trailing comma → invalid JSON, opencode-web crash-loops #309). The exact corruption: missing comma between sibling JSON keys, breaking parse at line 11 col 16.
  2. Run bash install.sh --all --non-interactive from a branch with fix(07-devtools): preserve trailing comma when rewriting opencode.json Light-Heart-Labs/DreamServer#901 merged.
  3. Phase 07-devtools enters the "Reinstall" branch (file exists) and the jq path (jq is installed).
  4. Install log line 101: ⚠ OpenCode config jq rewrite failed; leaving existing config in place.
  5. The broken file is unchanged. OpenCode CLI is still broken until the user manually deletes the file.

Impact

Medium. New installs from a clean state are unaffected by this issue — those go through the inline HEREDOC template at the top of the same function and produce a valid file. Only the upgrade path from a pre-Light-Heart-Labs#901 broken state is affected. But once a user is in this state, they don't get out of it without rm ~/.config/opencode/opencode.json and a re-run.

This is the kind of bug that produces "the installer says it succeeded but my devtools are still broken" support issues.

Suggested Approach

Option A (least invasive) — chain the sed fallback into the jq-failure branch by collapsing the two branches:

if command -v jq >/dev/null 2>&1 && jq --arg url ... '...' "$OPENCODE_CONFIG_DIR/opencode.json" > "$_opencode_tmp"; then
    mv "$_opencode_tmp" "$OPENCODE_CONFIG_DIR/opencode.json"
    ai_ok "OpenCode config updated (API key and URL refreshed)"
else
    rm -f "$_opencode_tmp" 2>/dev/null
    # narrow-sed fallback — runs when jq is absent OR when jq parse fails
    _sed_i "s|\"apiKey\": *\"[^\"]*\"|\"apiKey\": \"${_opencode_key}\"|" "..."
    _sed_i "s|\"baseURL\": *\"[^\"]*\"|\"baseURL\": \"${_opencode_url}\"|" "..."
    ai_ok "OpenCode config updated (API key and URL refreshed)"
fi

Caveat: this still leaves a broken file unchanged if the corruption is structural enough that the narrow-sed regexes don't match either (e.g. the missing comma in the test repro means the line containing apiKey doesn't quite match the sed pattern's whitespace expectations).

Option B (most robust) — when jq fails to parse, write a fresh config from the inline HEREDOC template at the top of the same function. This guarantees recovery from any prior corruption regardless of structure. Slightly more invasive but the only option that's actually deterministic.

I'd recommend B for predictability. A is fine if maintainers prefer minimal-diff fixes.

Cross-references


Filed during full-stack integration test of open PR stack Light-Heart-Labs#893–909 on Light-Heart-Labs/DreamServer@c0600ca3. Environment: WSL2 / Ubuntu 24.04 / jq 1.7.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinginstallerInstaller issueswsl2WSL2-specific issues

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions