Symptom
Pressing /models (or any path that shows model-selection-menu) returns:
Планировщик: 🔴 Не удалось получить список моделей
Log:
[ERROR] [ModelHandler] Error showing model menu: GrammyError:
Call to 'sendMessage' failed! (400: Bad Request: BUTTON_DATA_INVALID)
at async showModelSelectionMenu
(.../bot/menus/model-selection-menu.js:56:9)
Cause
model-selection-menu.js builds inline-keyboard callback_data as:
keyboard.text(labelWithCheck, `model:${model.providerID}:${model.modelID}`).row();
Telegram Bot API caps callback_data at 64 bytes. So any model whose
providerID + : + modelID exceeds ~53 chars blows the request.
In our case the offending entry lives in ~/.local/state/opencode/model.json
→ recent:
{
"providerID": "fireworks",
"modelID": "accounts/hubabuba3227-1hvtqlh/deployments/kpwpvuky"
}
model:fireworks:accounts/hubabuba3227-1hvtqlh/deployments/kpwpvuky
= 66 bytes → BUTTON_DATA_INVALID.
This is purely a function of the arbitrary model ID; it happens with any model
whose path is long (Fireworks accounts deployments, custom fine-tunes, etc.).
The model itself is otherwise valid; only the inline-menu rendering breaks.
Repro
- Configure an OpenCode provider whose
modelID is longer than ~53 chars on
the joined model:<providerID>:<modelID> string.
- Trigger that model so it lands in
~/.local/state/opencode/model.json →
recent.
- In Telegram, send
/models.
- Telegram responds with 400
BUTTON_DATA_INVALID; the bot catches it and
replies with the localised model.menu.error text.
Suggested fixes (any one)
- Shorten/tile the callback: store an opaque short id (e.g. base32 hash or
incrementing index) in callback_data, look up the real
{providerID,modelID} from an in-memory map keyed by that id.
- Skip / rewrite override labels so the joined payload fits 64 bytes; surface
an upsell in the menu text when truncation occurs.
- Add a model-id length validator at startup that warns or refuses to render
models whose callback_data would exceed 64 bytes.
Option 1 is the most robust; option 3 is the cheapest patch.
Environment
- Bot version: v0.22.1 (npm
@grinev/opencode-telegram-bot@latest)
- OpenCode CLI/opencode serve: 1.17.13
- Telegram Bot API: latest (2026-07-04)
- Confirmed: end-to-end Telegram polling and OpenCode serve are healthy; only
the inline-keyboard payload fails.
Symptom
Pressing
/models(or any path that showsmodel-selection-menu) returns:Планировщик: 🔴 Не удалось получить список моделей
Log:
Cause
model-selection-menu.jsbuilds inline-keyboardcallback_dataas:Telegram Bot API caps
callback_dataat 64 bytes. So any model whoseproviderID+:+modelIDexceeds ~53 chars blows the request.In our case the offending entry lives in
~/.local/state/opencode/model.json→
recent:{ "providerID": "fireworks", "modelID": "accounts/hubabuba3227-1hvtqlh/deployments/kpwpvuky" }model:fireworks:accounts/hubabuba3227-1hvtqlh/deployments/kpwpvuky= 66 bytes →
BUTTON_DATA_INVALID.This is purely a function of the arbitrary model ID; it happens with any model
whose path is long (Fireworks accounts deployments, custom fine-tunes, etc.).
The model itself is otherwise valid; only the inline-menu rendering breaks.
Repro
modelIDis longer than ~53 chars onthe joined
model:<providerID>:<modelID>string.~/.local/state/opencode/model.json→recent./models.BUTTON_DATA_INVALID; the bot catches it andreplies with the localised
model.menu.errortext.Suggested fixes (any one)
incrementing index) in
callback_data, look up the real{providerID,modelID}from an in-memory map keyed by that id.an upsell in the menu text when truncation occurs.
models whose callback_data would exceed 64 bytes.
Option 1 is the most robust; option 3 is the cheapest patch.
Environment
@grinev/opencode-telegram-bot@latest)the inline-keyboard payload fails.