Skip to content

Resuming a session does not restore the provider it was created against #537

Description

@emal-avala

Raised by Codex on #518 as a P1. Filing rather than fixing there, because it is not a regression from that PR — the same flaw is on main today.

The problem

Resuming a session restores its model name but not the provider it was created against:

// crates/cli/src/ui/modern/run.rs — and commands/mod.rs:1134 on main
if !data.model.is_empty() {
    st.config.api.model = data.model.clone();
}

QueryEngine::llm stays bound to the provider constructed from the current project's startup config. If the selected session was created with a different API base URL, authentication mode, or wire format, the next turn sends the restored model and conversation to the wrong endpoint.

Consequences, roughly in order of how bad they are:

  1. The turn fails — the endpoint does not recognise the model. Noisy, but safe.
  2. The turn succeeds against the wrong account. A model name valid on both providers routes the restored conversation — including its full history — to whichever one startup happened to configure. This is the case worth caring about: it is silent, and it exports conversation content across an account boundary the user believed they were switching away from.

Why it is not #518's bug

main already does the identical model-only restore in classic /resume:

  • crates/cli/src/commands/mod.rs:1134
  • crates/cli/src/commands/mod.rs:5995

The in-TUI session picker makes cross-session resume easy enough to hit routinely, so it raises the exposure — but the defect predates it.

Why it needs its own change

SessionData does not persist provider identity at all today — no base URL, auth mode, or wire format. So this cannot be fixed by reading the session harder; it needs:

  1. a persisted provider fingerprint on SessionData (with a migration path for sessions saved without one), and
  2. a decision at resume time: rebuild the provider from the session's config, or refuse the resume with a clear reason.

Refusing is likely the right first move — rebuilding a provider mid-session touches auth and credential scoping, and a session whose provider cannot be reconstructed should not silently run against a substitute.

Sessions saved before the fingerprint exists have to be treated as unknown provider, which argues for resuming them only when the destination config matches the current one, or warning explicitly.

Suggested acceptance

  • SessionData carries enough to identify the provider it ran under.
  • Resuming a session whose provider differs from the destination either rebinds correctly or is refused with a message naming the mismatch.
  • A session saved before this field exists resumes without crashing, and does not silently bind to a different provider.
  • Tests cover: same provider (resumes), different provider (refused or rebound), missing fingerprint (handled explicitly).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions