Skip to content

Media-group file uploads ignore chat_prefs-bound context — "no project context available" #562

@nathanschram

Description

Problem

Uploading two or more files at once via Telegram (a media group) failed with:

no project context available for file upload.

A single file upload to the same chat worked fine. First observed on the
channelo VPS (@hetz_channelo_bot, single project auditor-toolkit, owner
DM) — screenshot showed two documents sent together, then the bot replying with
the error.

Investigation

  1. Traced the error string to telegram/commands/file_transfer.py:_prepare_file_put_plan
    — it fires when resolved.context / resolved.context.project is None
    after cfg.runtime.resolve_message(...).
  2. resolve_message resolves a project from, in order: reply context →
    directives → ambient_contextdefault_project / chat_map. So the
    failure means all of those were empty for the upload.
  3. Compared the two upload code paths:
    • Single fileloop.py:route_messagebuild_message_context, which
      computes ambient_context via a 3-rung fallback ladder: topic-bound →
      chat_prefs chat-bound (/ctx) → topic-merged default.
    • Media group (2+ files) → coalesced by MediaGroupBuffer
      commands/media.py:_handle_media_group, which computed ambient_context
      from only topic_store + _topics_chat_project — it never consulted
      the per-chat ChatPrefsStore.
  4. Inspected channelo's config + state:
    • untether.toml has no default_project and the project has no
      chat_id
      (the config validator forbids a project chat_id equal to
      transports.telegram.chat_id, and channelo's project chat is the
      transport chat).
    • telegram_chat_prefs_state.json holds context_project: auditor-toolkit
      — i.e. the project is bound only via /ctx (chat_prefs).
  5. git log --follow -S on the _handle_media_group ambient-context block:
    only two commits ever touched it — today's fix and the pre-0.35.0
    takopi → untether rename. The gap is as old as the file.

Root cause

_handle_media_group never consulted ChatPrefsStore. On a deployment where
the project is resolvable solely via the /ctx-bound chat context — no
default_project, no chat_map entry — a media group resolved
ambient_context = Noneresolve_message found no project → the error.
Single-file uploads worked because their path (build_message_context) already
had the chat_prefs rung.

Not a v0.35.3 regression

The bug has existed since media.py was introduced (pre-0.35.0). It only
surfaces when the project resolves solely via /ctx-bound context. On lba-1
(multi-project) and nsd (per-project chat_idchat_map), media groups
resolve via default_project / chat_map regardless. The channelo
single-project-DM deployment
(set up ~2026-05-13) is the first config shape to
expose the latent bug — not a code change in any recent release.

Solution

Thread chat_prefs through the media-group path and mirror
build_message_context's fallback ladder:

  • telegram/commands/media.py_handle_media_group takes a new
    chat_prefs: ChatPrefsStore | None parameter and applies the topic-bound →
    chat_prefs chat-bound → topic-merged-default ladder (with a
    # keep in sync with loop.py:build_message_context comment).
  • telegram/loop.pyMediaGroupBuffer._flush_media_group forwards its
    already-held self._chat_prefs to handle_media_group(...).
  • tests/test_telegram_media_command.py — new regression test
    (test_media_group_uses_chat_prefs_bound_context): with a chat_prefs stub
    and topic_store=None, asserts the media-group path resolves the bound
    project. The pre-existing test_media_group_auto_put_* tests only checked
    delegation and would not have caught this.

Shipped in PR #563 (squash-merged to dev).

Other actions

  • Channelo immediate mitigation — added default_project = "auditor-toolkit"
    to channelo:~/.untether/untether.toml (hot-reloaded, no restart). This
    unblocks channelo now, ahead of the code fix reaching it via the next rc
    fleet rollout. Recommended as a belt-and-suspenders default for any
    single-project deployment.
  • CHANGELOG — entry added under ## v0.35.3### fixes.
  • Milestone — v0.35.3.

Follow-up (out of scope)

build_message_context and _handle_media_group independently implement
"compute ambient context for an incoming Telegram message". A shared
compute_ambient_context() helper would prevent the next instance of this
divergence class. An interim # keep in sync comment was added.

Affected files

  • src/untether/telegram/commands/media.py
  • src/untether/telegram/loop.py
  • tests/test_telegram_media_command.py

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:majorSignificant functionality broken or seriously degraded; workaround exists but painful

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions