Skip to content

session: titles never generated in v2 (no auto trigger + free-tier rejection) #58176

Closed

Description

@LucienChu

Summary

V2 never generates session titles. New sessions keep title = NULL ("Untitled session"), while v1.18.x auto-titled the same setup with the same model.

Environment

  • opencode version: 2.1.3
  • OS: macOS 26.5 (Darwin 25.5.0, arm64)
  • Terminal: Apple_Terminal, TERM=xterm-256color, COLORTERM=truecolor
  • Shell: /bin/zsh
  • Install/channel: Homebrew anomalyco/tap/opencode-v2 2.1.3, latest
  • Plugins: none

Reproduction

Global config exposes only the free opencode provider (no entries in opencode auth list):

{ "model": "opencode/big-pickle", "small_model": "opencode/big-pickle" }
  1. opencode run "reply with exactly: hi"
  2. opencode session list → new session shows Untitled session; session_v2.title is NULL.
  3. Force generation: opencode api patch /api/session/<id> --data '{"title":""}' → title stays NULL; no output, no log line.

Reproduces with a fresh data dir and --standalone, so it is not migration or DB related.

Expected Behavior

The session gets a generated title after the first user message, as in v1 (which logged agent=title small=true).

Actual Behavior

Two separate defects:

1. The title request is rejected by the free-tier gate.

opencode run --standalone --agent title "say ok"
# Error: OpenCode's free tier can only be used from within OpenCode. Download it at https://opencode.ai and keep building.
opencode run --standalone --agent summary "say ok"    # same error
opencode run --standalone --agent build "say ok"      # works
opencode run --standalone --agent compaction "say ok" # works

Same for every opencode/* model, so it is the maintenance request shape, not the model. SessionTitle.attempt swallows provider errors and returns undefined without logging, so the failure is invisible.

2. No automatic trigger.

Inspecting the 2.1.3 binary, the only caller of title generation is the session.update handler when title is an empty string, and the only UI caller of that is /rename submitted empty (session.title.generate). Nothing runs after the first message.

Additional Context

  • opencode debug agents resolves the agent correctly: title → {providerID: "opencode", id: "big-pickle"}.
  • Removing small_model changes nothing; it is deprecated in favor of agents.title.model.
  • v1 logs contained message=stream ... small=true agent=title; v2 logs contain no title activity at all.

Activity

  1. opencode-agent commented on Oct 22, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I reproduced this on 2.1.3 with a fresh data dir and only the free opencode/big-pickle model:

    • opencode run --standalone --agent build "say ok" works.
    • opencode run --standalone --agent title "say ok" fails with OpenCode's free tier can only be used from within OpenCode....
    • After opencode run --standalone "reply with exactly: hi", session_v2.title stays NULL, and nothing is logged.

    One correction on the second point: I don't think the automatic trigger is missing. With the same 2.1.3 build and a local mock provider, a title request is sent automatically after the first message and the session gets a title. That request has the same x-opencode-* headers and user-agent as a normal turn; its body has no tools and no output limit. So the missing titles look like the free-tier rejection of the title request (your first point), which SessionTitle.attempt then drops silently.

    I couldn't test the free tier from a source build of the latest dev (09a1db3), because the free tier rejects every request from a local build. 2.1.3 is the current release, and the title code is unchanged on dev. The script below reproduces it.

    Related: #49592 (the same rejection of the title/compaction agents on v1, fixed on the server at the time) and #55575.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Repro for #58176: v2 session titles stay NULL on the free `opencode` provider.
    # Needs network access to opencode.ai (free tier, no credentials).
    # OC="opencode" by default; set OC to run another build, e.g. from source.
    set -u
    OC=${OC:-opencode}
    ROOT=$(mktemp -d /tmp/opencode/r58176.XXXX)
    export XDG_DATA_HOME=$ROOT/data XDG_CONFIG_HOME=$ROOT/config XDG_STATE_HOME=$ROOT/state XDG_CACHE_HOME=$ROOT/cache
    mkdir -p "$XDG_CONFIG_HOME/opencode" "$ROOT/project"
    echo '{"model":"opencode/big-pickle"}' > "$XDG_CONFIG_HOME/opencode/opencode.json"
    cd "$ROOT/project"
    $OC --version
    
    echo "== normal turn (build agent): works"
    $OC run --standalone --agent build "say ok" 2>&1 | tail -2
    echo "== same model, title agent: rejected by the free-tier gate"
    $OC run --standalone --agent title "say ok" 2>&1 | tail -2
    
    echo "== new session: the automatic title request is sent, but the title stays NULL"
    $OC run --standalone "reply with exactly: hi" 2>&1 | tail -1
    sleep 5
    DB=$(find "$XDG_DATA_HOME" -name opencode.db | head -1)
    bun -e "const {Database}=require('bun:sqlite');console.log(new Database('$DB',{readonly:true}).query('select id,title from session_v2').all())"

    Run it: bash repro.sh

  2. martinochoaarboin commented on Oct 23, 2026

    @martinochoaarboin

    Additional data point from a $0-balance Console account (provider opencode), Windows 11, npm global install.

    The default small model is paid, and it fails

    opencode run --agent title --model opencode/gpt-6-luna "say ok"
      -> Error: Upstream request failed: Insufficient account funds
    
    opencode run --agent title --model opencode/deepseek-v4.1-flash-free "say ok"
      -> responds normally
    

    Model.small("opencode") resolves to gpt-6-luna (family gpt-luna, paid). So on this account the first title attempt can never succeed, and the fallback to the primary model does not produce a title either.

    What I could rule out

    • Pointing agents.title.model at the free model that works above does not restore titles (2.1.3). The same config also fails on 2.1.2, so in this setup this is not a 2.1.3-only regression.
    • PATCH /api/session/<id> with {"title":""} returns 204, leaves the title NULL, and writes no log line at any level.
    • /api/agent/title correctly reports the configured model (not null), so the override is being applied.
    • GET /api/session/<id> reports model: null on the affected sessions while the messages carry the model (flint-40b-free, deepseek-v4.1-flash-free).

    Why nothing surfaces

    In SessionTitle.generate, attempt marks failed on a provider error and returns undefined, and the caller (FiberMap.run in session/runner/llm.ts) discards failures. Any early return (!selection, no first user message, empty title) is equally silent. Logging the failure reason / early-return branch there would make this class of report diagnosable from the log alone.

  3. sinneycz commented on Oct 23, 2026

    @sinneycz

    Additional data point from the Windows desktop app (2.1.3, free opencode/deepseek-v4.1-flash-free). The automatic title request is sent, and it fails at the free-tier gate.

    Environment: Windows 10.0.19044 x64, desktop app 2.1.3 (channel latest), no plugins, no custom config.

    I instrumented the session http.request / http.response hooks. Title request:

    • POST https://opencode.ai/inference/openai/v1/chat/completions
    • body keys: model, messages, stream, stream_options
    • headers identical to a normal turn: authorization: Bearer public, user-agent: opencode/latest/2.1.3/desktop, x-opencode-client: desktop, same x-opencode-* session headers
    • response: 403
      {"type":"error","error":{"type":"FreeTierError","message":"OpenCode's free tier can only be used from within OpenCode. Download it at https://opencode.ai and keep building."}}

    Same session and model:

    • normal turn: model, messages, tools, stream, stream_options, max_completion_tokens → 200
    • POST /api/session/<id>/generate: model, messages, tools, stream, stream_options → 200

    The only meaningful difference is the missing tools (and max_completion_tokens), so this points at the request body shape rather than auth/headers. The title request is tool-less by design (packages/core/src/session/title.ts), and SessionTitle.attempt swallows the provider error.

    PATCH /api/session/<id> with {"title":""} returns 204 and leaves title NULL; a non-empty title renames correctly.

    Workaround for free-tier users: a title hook that calls ctx.generate.text (a tool-bearing request, same shape as the 200 /generate call) and assigns event.result. I haven't run that end-to-end yet, but the captured /generate request is accepted.

  4. xhugoliu commented on Oct 23, 2026

    @xhugoliu

    Cross-posting extras from #58411 (auto-flagged as a duplicate of this one):

    Control-arm observation (2.1.3, same machine/day, 5 runs without --title)

    arm runs title after turn
    opencode/glm-5.3-flash-free 3 NULL (New session — <ts>), still NULL at t+30s
    opencode/deepseek-v4.1-flash-free 1 NULL
    custom local-proxy Gemini provider, gemini-3.5-flash-lite (control) 1 auto-generated fine

    The control result supports the free-tier-gate root cause discussed here and rules out my initial "title call races the first turn" hypothesis (#58411) — the non-opencode provider titles fine even though its title call also runs concurrently with the first turn.

    Source-level confirmation of the silence (dev branch)

    • packages/core/src/session/runner/llm.ts (~L171): the title fiber is started via FiberMap.run(titles, sessionID, title.generate(sessionID), { onlyIfMissing: true }) with no failure observer.
    • packages/core/src/session/title.ts: every failure path exits silently — providerError → failed = true, Effect.catchTag("AI.Error", ...) → failed = true, then if (failed) return and if (!title) return — and the module contains no log calls at all.
    • Title attempts persist no session message record in either the failing or the successful arm, so nothing is observable session-side.

    Two notes that may help triage beyond this issue:

  5. armandolazarte commented on Oct 26, 2026

    @armandolazarte

    Adding an occurrence on Windows with the free opencode/* gateway models.

    Environment

    • opencode v2.1.4 (channel latest, updated automatically from v1.19.3 between Oct 22 and Oct 26)
    • Windows 11 x64
    • Model: opencode/glm-5.3-flash-free (free provider, session model)

    What we observed

    1. On v1.19.3 the server logged automatic title generation for every top-level session (message=stream ... agent=title small=true); the last example is dated 2026-10-22.
    2. After the automatic update to v2.1.4, new sessions keep title = null ("New session - ").
    3. Verified live through the HTTP API with a fresh session and the same free model: the model answered normally, the turn completed with idle, outcome: succeeded, and the session title was still null more than 75 seconds later. No error surface anywhere (server log clean, consistent with FreeTierError being silently dropped).

    Matches the described root cause: the title request is sent, the free tier rejects it with 403, and the failure is not logged or surfaced.

    Happy to test a build with #57624 or any other fix on Windows if useful.

  6. flaviovich commented on Oct 27, 2026

    @flaviovich

    Confirming on 2.1.4, Linux desktop (opencode/deepseek-v4.1-flash-free, 5/5 sessions with title IS NULL after the upgrade) and adding a workaround, since the previous confirmations only describe the symptom.

    The automatic trigger does work in 2.1.4. Only the title model is the problem: point the title agent at a non-opencode provider and titles come back while the chat stays on the free gateway.

    {
      "small_model": "google/gemini-3.5-flash-lite",
      "agent": {
        "title": { "model": "google/gemini-3.5-flash-lite" }
      }
    }
  7. iamlijiu commented on Oct 27, 2026

    @iamlijiu

    Confirming this still reproduces on v2.2.0 with the free opencode provider.

    Environment

    • opencode 2.2.0 (npm @opencode/cli, channel latest)
    • Linux 6.6.87.2-microsoft-standard-WSL2 x64
    • Terminal: TERM=xterm-256color (no COLORTERM / TERM_PROGRAM)
    • Shell: /bin/bash
    • Active plugins: none
    • Providers: no authenticated integrations; only the built-in free opencode provider

    Steps

    1. opencode run "reply with exactly: ok" in a fresh cwd (default model opencode/big-pickle)
    2. opencode api get /api/session

    Observed

    • New session created 2026-10-28T00:33:08Z: the turn completes normally (ok), title = null.
    • Interactive session on opencode/deepseek-v4.1-flash-free: same, title = null.
    • Sessions created on v1.x on the same machine still carry titles (e.g. opencode 卸载后重新安装, Version query), so this is a v2 regression and not a migration artifact.
    • No FreeTierError / 403 line appears in ~/.local/share/opencode/log/opencode.log, consistent with the title request failing silently.

    Workaround that works today: Ctrl+R (session.rename) renames manually.

    Related: #57624 would at least label such sessions from the first prompt when generation fails.

  8. LucienChu commented on Nov 1, 2026

    @LucienChu
    Author

    Closing this issue as resolved. Automatic session title generation is now functioning correctly.

    I initially worked around the issue by manually using /rename <session-title>, but verified on October 30, 2026, that auto-generation has recovered across both macOS and WSL environments.

    Verification Details

    • Version: v2.1.3 (identical across environments)
    • Configuration: No changes made to opencode.config

    The command opencode session list --max-count 1 --format json was run after executing test prompts on both machines. Here are the individual results:

    WSL Environment:

    [
      {
        "id": "ses_e6849c53affeP3hJEoH0j6QFF0",
        "title": "Christmas 2026 date",
        "updated": 1793546667095,
        "created": 1793546664652,
        "projectId": "84bc16e9e6e61777ce8b35999354d42a1cfe5c55",
        "directory": "/home/lucienchu"
      }
    ]

    Mac

    [
      {
        "id": "ses_e7028250cffeK20SOl9sz427sE",
        "title": "Zustand guide for React",
        "updated": 1793414664175,
        "created": 1793414650675,
        "projectId": "cf26dc619c2b1e8cb5a0dfe0849513026b6ff957",
        "directory": "/Users/lucien"
      }
    ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions