Skip to content

Prompt leaks between the sessions #35587

Closed as not planned
Closed as not planned

Description

@Blade1024

Description

I am experiencing an issue when the promt from one session is appearing in the history of another one.

Plugins

None

OpenCode version

1.17.14

Steps to reproduce

  1. Start two independent sessions
  2. Execute commands in both
  3. Recall commands with the arrow up

Screenshot and/or share link

No response

Operating System

Ubuntu 24.04

Terminal

gnome term

Activity

  1. github-actions commented on Jul 6, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of or related to existing issues. Please check:

    If your issue is specifically about the TUI input prompt history (the arrow-up/down history in the opencode prompt editor) being shared across sessions, this appears to be the same underlying behavior. If it is about something different (e.g. AI conversation context bleeding between sessions), please clarify in the issue.

  2. Blade1024 commented on Jul 6, 2026

    @Blade1024
    Author

    Actually, if I break one of the sessions, it repeats the history of the active one. Crazy....

  3. fariello commented on Jul 15, 2026

    @fariello

    Confirming this is still present in v1.18.2 (tag v1.18.2, commit 70b56a0a9), and adding a source-level root cause + fix direction. This is the same underlying behavior as the auto-closed #25811 (global, not project-scoped) and #19536 / #19436 (multi-instance clobbering) — none of which were fixed before the inactivity bot closed them.

    Root cause

    The TUI prompt-recall history is persisted to a single global file with no project/session/directory key and no locking:

    $XDG_STATE_HOME/opencode/prompt-history.jsonl
    

    Every concurrent opencode TUI process reads from and writes to this same file regardless of project/dir/session.

    • Path is keyed only by the global state dir + a constant filename — packages/tui/src/prompt/history.tsx:52-53:
      const paths = useTuiPaths()
      const historyPath = path.join(paths.state, "prompt-history.jsonl")
      paths.state = global.state = $XDG_STATE_HOME/opencode (packages/tui/src/app.tsx:260, packages/core/src/global.ts), identical for every invocation.
    • The persisted record PromptInfo (history.tsx:9-25) stores only input/mode/parts — no project or session id, so it can't be filtered even in principle.
    • Read path loads the whole file unfiltered on mount (history.tsx:54-61); write path appends on every submit (history.tsx:85-108, called from packages/tui/src/component/prompt/index.tsx:1121,1273); recall bindings are Up/Down → history.move() (config/keybind.ts:199-200,402-403; component/prompt/index.tsx:879,915).

    That fully explains the arrow-up recall showing another session's prompts.

    Secondary defect (the "#19536" clobbering)

    The over-cap branch rewrites the whole file from the process's stale in-memory snapshot (history.tsx:103-104), overwriting entries other instances appended since this process last read — a lost-update race. No lock / atomic write on this path (packages/tui/src/util/persistence.ts:12-20), even though the repo already has Flock (packages/core/src/global.ts, Flock.setGlobal({ state })) and writeJsonAtomic (persistence.ts:22-33, unused here).

    Scope / not-scope

    • Session transcripts are not affected — DB rows are correctly isolated by project_id/directory. I verified two concurrent sessions in different projects: distinct project_id, distinct directory, distinct message counts. Only the prompt-recall file leaks.
    • Same global-file design affects siblings under paths.state and may show the same bleed: prompt-stash.jsonl (prompt/stash.tsx:36), frecency.jsonl (prompt/frecency.tsx:42), model.json/session.json (context/local.tsx:164,420), kv.json (context/kv.tsx:15) — i.e. the umbrella client state clobbered w/ multiple instances of opencode #19436.

    Suggested fix direction

    1. Scope per project. Key the filename by project.id (already available via useProject(), packages/tui/src/context/project.tsx:49-50,78-80), falling back to a hash of worktree/cwd for unsynced/non-git dirs; e.g. paths.state/prompt-history/<key>.jsonl. Resolve reactively — project.id is briefly undefined before the first sync() (project.tsx:38-53), so don't capture a single const at init.
    2. Fix the race. Prefer append-only on the hot path + enforce the 50-entry cap lazily on read (the mount-time self-heal at history.tsx:58-60 already trims). If a rewrite is kept, guard with Flock and/or a JSONL-preserving atomic write.

    Tests worth adding: two-projects-concurrent (leak), same-project interleaved over the 50-entry cap (race), non-git dir and cd-within-repo (isolation vs continuity).

    Workaround for affected users

    Give each instance its own state dir:

    export XDG_STATE_HOME="$HOME/.local/state/oc-$(basename "$PWD")"
  4. github-actions commented on Sep 14, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

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