Skip to content

bot: Telegram photos dropped as "model doesn't support image input" while an OpenCode v2 location is cold #257

Closed

Description

@depler

Summary

Sending a Telegram photo as the first message after a long idle period is silently dropped with Model ... doesn't support image input, even though the model does support image input. On OpenCode v2 the per-directory "location" is unloaded after ~60 minutes of inactivity and rebooted lazily on the next request; during that cold boot the providers/models list is momentarily empty, and getModelCapabilities() treats that empty snapshot as "the model has no image support".

Environment

  • opencode version: 2.0.18 (CLI, managed server, bundled @opencode/client all 2.0.18; OPENCODE_SERVER_VERSION=v2)
  • OS: Linux 6.8.0-139-generic (x86_64), Ubuntu 24.04
  • Node.js: v24.21.0
  • opencode-telegram-bot: 0.26.0 (global npm, systemd opencode-bot.service, server on 127.0.0.1:49374)
  • Shell: /usr/bin/bash
  • Install/channel: release (systemd, OPENCODE_AUTO_RESTART_ENABLED=false)
  • Active plugins: opencode-cmd-provider 2.1.9 (provider commandcode), tps-counter@git+https://github.com/depler/opencode-tps-counter.git
  • Model: commandcode/deepseek/deepseek-v4.1-flash (image-capable; see Additional Context)

Reproduction

  1. Use an OpenCode v2 server with a plugin provider, and leave a location (project directory) idle for more than ~60 minutes. The server unloads it:

    [server] location services evicted  directory=/opt
    [server] location services evicted  directory=/root
    
  2. Send a photo to the bot as the first message after the idle period.

  3. The next request reboots the location lazily; models register a few seconds later:

    [server] location services booted   directory=/root  durationMs=1271
    [server] event provider.updated     directory=/root
    
  4. The bot's capability lookup lands inside that cold window.

Expected Behavior

The photo should be forwarded. When the providers list is not yet populated, the bot should either retry briefly (as the server-ready wait does) until the model list is non-empty, or not treat unknown capabilities as "image unsupported". Silently dropping the attachment is the worst outcome.

Actual Behavior

The photo is silently dropped. The bot logs a warning and skips the attachment:

[WARN] [ModelCapabilities] Providers list has no models; not caching commandcode/deepseek/deepseek-v4.1-flash
[WARN] [Bot] Model commandcode/deepseek/deepseek-v4.1-flash doesn't support image input

Sending the exact same photo again a moment later succeeds, so the model and the bot's mapping are fine — only the first, cold-location attempt fails.

Additional Context

Root cause

  • dist/app/services/model-capabilities-service.js → getModelCapabilities() returns null when every provider has zero models. It deliberately does not cache that ("not caching"), but it still returns null to the caller.
  • supportsInput(capabilities, "image") returns false for null, so prepareTelegramPhotos() in dist/bot/handlers/prompt.js logs "doesn't support image input" and drops the photo.

Cold-location timeline (server log is MSK = UTC+3)

19:20:22.193  location services evicted  directory=/opt
19:20:22.438  location services evicted  directory=/root
19:21:02.915  location services booted   directory=/opt   durationMs=256
19:21:04.386  location services booted   directory=/root  durationMs=1271
19:21:04.396  [Bot] Providers list has no models; not caching commandcode/deepseek/deepseek-v4.1-flash
19:21:04.397  [Bot] Model commandcode/deepseek/deepseek-v4.1-flash doesn't support image input
19:21:07.828  provider.updated          directory=/root   (models registered only here)

The capability request arrived 10 ms after the /root location finished booting but ~3.4 s before the plugin registered its models, so /api/model was still empty.

The model is image-capable

  • Plugin catalog ([email protected]): "deepseek/deepseek-v4.1-flash": ["text", "image"].
  • Live GET /api/model: "capabilities": { "tools": true, "input": ["text","image"], "output": ["text"] }.
  • Running the bot's own toV1Providers mapper over the live response yields capabilities.input.image === true.

Notes

  • OpenCode v2 unloads a location after ~60 minutes of inactivity (sweep every 1 minute). This is a normal lifecycle, not a crash or a server restart, so a startup readiness wait does not cover it.
  • Workaround without a bot change: send the photo a second time (the first attempt warms the location and the empty result is not cached), or send any text message before the photo after a long idle.
  • Related: [Bug]: Models from plugin providers vanish after restart until the 10-minute catalog cache expires #255 (catalog cached from a partial snapshot after restart) — same class of "empty/partial provider snapshot treated as authoritative", but a different code path (getModelCapabilities vs the model catalog cache).

Activity

  1. depler commented on Sep 27, 2026

    @depler
    Author

    Cross-link: the upstream server-side cause is tracked in anomalyco/opencode#51701 (empty /api/model / /api/provider while a Location reboots after the 60-minute idle eviction; same class as anomalyco/opencode#36117).

    Client-side this bot issue is the observable impact: getModelCapabilities() gets an empty providers list, returns null, and supportsInput(null, "image") drops the attachment. Fixing the bot to retry on an empty list would make it resilient to this regardless of the server fix.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions