Skip to content

OpenCode V2: every turn refused in silence with 0.43.2 — v2 refuseIfUnsafe / "context storage is not durable" (outcome=interrupted, no assistant message) #546

Closed

Description

@expnn

Summary

On OpenCode V2 (2.0.18), loading @cortexkit/opencode-magic-context (npm latest 0.43.2, freshly installed) makes every session turn die in silence: the user message is stored, the turn is refused before the provider call, the host records idle {outcome:"interrupted"} ~60–360 ms later, and no assistant message is ever produced. The TUI shows no reply and no visible error. Same symptom class as the closed #493; still reproducible with the current npm release on this host.

Environment

  • OpenCode V2: 2.0.18 (both ~/tools/ocv2/opencode and ~/.opencode/bin/opencode2), Linux
  • magic-context: 0.43.2 — reproduced with the previously cached install and with a fresh install after clearing ~/.cache/opencode/npm/@cortexkit/opencode-magic-context@latest (new cache dir created, version verified 0.43.2)
  • V1: 1.18.32 with the same plugin on the same host works normally (its Magic Context transforms run; see the plugin log)
  • Context store: ~/.local/share/cortexkit/magic-context/context.db (118 MB, journal_mode=wal, WAL ~6 MB, actively written; the store is V1-era — the plugin log contains historical refusing to migrate … from upstream migration v79 to v85 lines from 2026-09-24)
  • ~/.local/share/opencode/opencode.db is ~2.7 GB and shared by V1+V2 on this host

Symptom / evidence

session_message rows (~/.local/share/opencode/opencode.db) for a refused turn:

seq 4  user  "say PB-shared"              t=…4983132
seq 5  idle  {"outcome":"interrupted"}    t=…4983492   ← 360 ms later; no assistant row

Console trace at refusal time (observed in a standalone V2 run):

[magic-context] v2 refuseIfUnsafe warn: context storage is not durable

Timeline: last successful assistant message = 2026-09-26 21:28:52 (before V2 had ever loaded plugins on this host); the V2 plugin caches were first populated 21:34–35 that day; every V2 turn since has been interrupted.

Controlled experiment (minimal repro)

Standalone V2 server (opencode --standalone, isolated XDG_CONFIG_HOME) with a minimal opencode.json containing exactly one plugin entry; submit a message; read session_message.outcome:

plugin loaded outcome
(none) succeeded — assistant reply generated (tokens billed)
only oh-my-opencode-slim succeeded
only a local herdr reporting plugin (V2 setup() no-op) succeeded
only @cortexkit/opencode-magic-context@latest (fresh 0.43.2) interrupted, no assistant message

Code pointers (master)

  • packages/plugin/src/v2/hooks/context.ts:709 and :838 — if (!db || !isDatabasePersisted(db)) throw new Error("context storage is not durable");
  • packages/plugin/src/v2/hooks/context.ts:277 — reportPreProviderRefusal() → console.warn("[magic-context] v2 refuseIfUnsafe", error); its comment states the host then stores the turn as interrupted and the user simply never receives a reply — no error on screen and no assistant message.
  • packages/plugin/src/v2/hooks/context.ts:1291–1305 — refusals are thrown as V2ContextRefusal (a fail-open branch exists only for compactionOff).
  • packages/plugin/src/features/magic-context/storage-db.ts:2529 — isDatabasePersisted(db) returns a WeakMap flag set during database open; getDatabasePersistenceError(db) holds the reason and is not surfaced anywhere user-visible.
  • On this host the plugin's own session log ($TMPDIR/opencode/magic-context/magic-context.log) contains no v2 refusing this turn before the model call lines for the V2 refusals — the only trace is the console warning.

Related

Expected

Repro steps

  1. V2 config with only this plugin: {"$schema":"https://opencode.ai/config.json","plugin":["@cortexkit/opencode-magic-context@latest"]}
  2. opencode --standalone (or any V2 server) and send any prompt.
  3. Observe: no assistant reply; session_message = user → idle {outcome:"interrupted"}; console = v2 refuseIfUnsafe + context storage is not durable.

Activity

  1. magic-alfonso commented on Sep 27, 2026

    @magic-alfonso

    Thanks for the detailed report and the controlled experiment. They point at a likely cause. I haven't confirmed it on your host yet, so please check the two things below.

    Likely cause. OpenCode 1 and OpenCode 2 share one Magic Context database (context.db). The refusing to migrate … from upstream migration v79 to v85 lines in your log mean the database is still at an old schema version. 0.43.2 needs version 90, so the first OpenCode 2 process that opens it has to migrate it. Magic Context deliberately refuses to migrate while another OpenCode server or Pi process is still running an older plugin build against the same database: migrating under it would make that older host fail closed. You run OpenCode 1.18.32 with the plugin on the same host, so if that OpenCode 1 server loads an older Magic Context build, OpenCode 2 refuses to migrate on every start. Magic Context then has no usable storage in that process, and every turn is refused. That matches what you see.

    Where the real reason is logged. OpenCode 2 writes Magic Context's log to a separate path, $TMPDIR/opencode2/magic-context/magic-context.log, not $TMPDIR/opencode/magic-context/magic-context.log. That's why you found no refusal lines. Could you check the OpenCode 2 log for a line starting with [magic-context] storage fatal: or upstream migration lane at boot from a recent start, and paste it here? If this is the cause, it names the blocking process ID.

    Which build OpenCode 1 loads. Could you also share the Magic Context entry from your OpenCode 1 config (is it pinned to a version?), and the output of npx @cortexkit/magic-context@latest doctor?

    Workaround, if the log confirms it. Stop the OpenCode 1 server, or update its cached plugin to 0.43.2 and restart it. Then start OpenCode 2 once, so it migrates the database with nothing old attached. After that, both hosts run on 0.43.2 against the same database.

    What we'll fix regardless. A turn refused like this must not end in silence. The refusal should carry the actual reason (the migration was refused because a named process still uses an older build) instead of the generic "context storage is not durable", and doctor should report this situation directly. I'll follow up here with the fix.

  2. magic-alfonso commented on Sep 28, 2026

    @magic-alfonso

    This is in 0.44.0. When OpenCode 2 couldn't open context.db, every turn was refused with nothing on screen, and the log said only "context storage is not durable". Now:

    • the refusal names its cause: the process that blocks the migration (its kind and PID), or a database newer than this build, with both schema versions and what to do;
    • a connected Magic Context TUI shows it as a toast on every refused turn, and once per session and cause a [Magic Context storage notice] is stored in the conversation. It is never sent to the model;
    • the open is retried at most once every five seconds, so once the blocker is gone a later turn migrates the database and goes through without a restart. Magic Context's tools still need an OpenCode restart after that, and a notice says so;
    • doctor reads the schema version built into every cached Magic Context copy on both OpenCode 1 and OpenCode 2. If a copy is behind the database, the check fails and names the directory and how to refresh it.

    For your setup: update both hosts. On OpenCode 2 use /plugins (ctrl+u on Magic Context) or opencode plugin update, and refresh OpenCode 1's cached plugin as well. Stop any OpenCode 1 server still running an older build, restart both hosts on 0.44.0, and run npx @cortexkit/magic-context@latest doctor --fix. We never confirmed the cause on your host, so if turns are still refused on 0.44.0, the refusal now says why. Please paste it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions