[GH-ISSUE #194] bug: multimodal content (image attachment) 500s /v1/chat/completions — list bound to a TEXT column #114

Closed
opened 2026-10-18 14:03:03 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @penradioGables on GitHub (Jul 28, 2026).
Original GitHub issue: https://github.com/open-webui/computer/issues/194

Summary

Attaching or pasting an image into an Open WebUI chat backed by a cptr workspace model makes POST /v1/chat/completions fail before the agentic loop starts. The reply never arrives and the UI shows no error — the chat just sits there.

Verified still present in v0.9.15. cptr/routers/gateway.py is byte-identical between the v0.9.10 and v0.9.15 tags, so the line numbers below apply to current main.

This is the same underlying assumption as #78 (closed), in a different place. That issue's report closed with "Worth auditing other spots in the message path that assume content is a str" — this is one of those spots, and it is still live.

Root cause

OWUI, like any OpenAI-compatible client, sends content as a plain string for text-only turns but as a list of typed parts as soon as an image is attached:

{"role": "user", "content": [
  {"type": "text", "text": "what is in this screenshot?"},
  {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
]}

gateway.py::_resolve_messages takes that value raw (L277–284):

    # Extract user content
    user_content = ""
    if messages:
        last_user = next(
            (m for m in reversed(messages) if m.get("role") == "user"),
            None,
        )
        user_content = (last_user.get("content", "") if last_user else "") or ""

and passes it straight into ChatMessage.create(content=user_content) (L321–327). ChatMessage.content is Column(Text, nullable=False), so binding a list raises:

sqlite3.ProgrammingError: Error binding parameter 5: type 'list' is not supported
sqlalchemy.exc.ProgrammingError: (sqlite3.ProgrammingError) Error binding parameter 5: ...
  [INSERT INTO chat_messages (id, chat_id, parent_id, role, content, model, done, ...)]

The exception escapes the endpoint before anything is streamed, which is why the client sees no error at all.

_ensure_chat carries the same assumption independently (L702–708) — it slices the content to build a title, which on a list yields a list, then calls .strip() on it:

        first_user = next(
            (m.get("content", "")[:50] for m in messages if m.get("role") == "user"),
            None,
        )
        if first_user:
            title = first_user.strip() or title

That path only fires when the first message of a brand-new chat carries the image, which is why the failure usually presents as the sqlite error rather than an AttributeError.

Reproduction

Any Open WebUI chat against a cptr workspace model, with one pasted image. Reproduced on 0.9.10 against a running instance (SQLite backend); the crashing code is unchanged in v0.9.15.

Suggested fix

The codebase already has the right helper — cptr/utils/agents/prompts.py::message_text() flattens exactly this shape and is used by the agent adapters. gateway.py simply never imports it. Using it in both places above fixes the crash.

Worth noting separately: even once the crash is fixed, images are still discarded on this path. cptr's own prepare_agent_attachments() reads images from ChatMessage.meta["files"], so the gateway would need to decode inline data: URIs into storage and populate that key for attachments to actually reach the agent rather than merely not crash.

Happy to open a PR if useful.

Originally created by @penradioGables on GitHub (Jul 28, 2026). Original GitHub issue: https://github.com/open-webui/computer/issues/194 ### Summary Attaching or pasting an image into an Open WebUI chat backed by a cptr workspace model makes `POST /v1/chat/completions` fail before the agentic loop starts. The reply never arrives and the UI shows no error — the chat just sits there. **Verified still present in v0.9.15.** `cptr/routers/gateway.py` is byte-identical between the `v0.9.10` and `v0.9.15` tags, so the line numbers below apply to current `main`. This is the same underlying assumption as #78 (closed), in a different place. That issue's report closed with *"Worth auditing other spots in the message path that assume `content` is a `str`"* — this is one of those spots, and it is still live. ### Root cause OWUI, like any OpenAI-compatible client, sends `content` as a plain string for text-only turns but as a **list of typed parts** as soon as an image is attached: ```json {"role": "user", "content": [ {"type": "text", "text": "what is in this screenshot?"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} ]} ``` `gateway.py::_resolve_messages` takes that value raw (L277–284): ```python # Extract user content user_content = "" if messages: last_user = next( (m for m in reversed(messages) if m.get("role") == "user"), None, ) user_content = (last_user.get("content", "") if last_user else "") or "" ``` and passes it straight into `ChatMessage.create(content=user_content)` (L321–327). `ChatMessage.content` is `Column(Text, nullable=False)`, so binding a list raises: ``` sqlite3.ProgrammingError: Error binding parameter 5: type 'list' is not supported sqlalchemy.exc.ProgrammingError: (sqlite3.ProgrammingError) Error binding parameter 5: ... [INSERT INTO chat_messages (id, chat_id, parent_id, role, content, model, done, ...)] ``` The exception escapes the endpoint before anything is streamed, which is why the client sees no error at all. `_ensure_chat` carries the same assumption independently (L702–708) — it slices the content to build a title, which on a list yields a *list*, then calls `.strip()` on it: ```python first_user = next( (m.get("content", "")[:50] for m in messages if m.get("role") == "user"), None, ) if first_user: title = first_user.strip() or title ``` That path only fires when the *first* message of a brand-new chat carries the image, which is why the failure usually presents as the sqlite error rather than an `AttributeError`. ### Reproduction Any Open WebUI chat against a cptr workspace model, with one pasted image. Reproduced on 0.9.10 against a running instance (SQLite backend); the crashing code is unchanged in v0.9.15. ### Suggested fix The codebase already has the right helper — `cptr/utils/agents/prompts.py::message_text()` flattens exactly this shape and is used by the agent adapters. `gateway.py` simply never imports it. Using it in both places above fixes the crash. Worth noting separately: even once the crash is fixed, images are still discarded on this path. cptr's own `prepare_agent_attachments()` reads images from `ChatMessage.meta["files"]`, so the gateway would need to decode inline `data:` URIs into storage and populate that key for attachments to actually reach the agent rather than merely not crash. Happy to open a PR if useful.
Author
Owner

@owui-terminator[bot] commented on GitHub (Jul 28, 2026):

🔍 Related Issues Found

I found some existing issues that might be related. Please check if any of these are duplicates or contain helpful solutions:

  1. 🟣 #78 TypeError: expected string or bytes-like object, got 'list' when sending a message with an image attachment
    This is the closest prior report of the same underlying bug class: multimodal message content arriving as a list instead of a string and crashing a cptr message-path handler. The new issue simply identifies a different code location (gateway.py) that makes the same assumption.
    by chhotu-claw

  2. 🟣 #153 bug: chat_messages INSERT crashes when Open WebUI models array reaches the scalar model column — message rows silently dropped
    Directly related because it is another chat persistence crash caused by binding a Python list into a scalar TEXT column during chat_messages INSERT. The new issue’s sqlite error is the same failure mode, just triggered by image-bearing content rather than model.
    by penradioGables

  3. 🟣 #50 Gateway connection hangs in OpenWebUI
    Related at the symptom level because both describe OpenWebUI-to-cptr /v1/chat/completions requests that hang or never complete, indicating gateway-side failures in the same request path. However, this one appears broader and not specifically tied to multimodal attachments.
    by naqerl


💡 If your issue is a duplicate, please close it and add any additional details to the existing issue instead.

This comment was generated automatically. React with 👍 if helpful, 👎 if not.

<!-- gh-comment-id:5104563388 --> @owui-terminator[bot] commented on GitHub (Jul 28, 2026): <!-- terminator-bot:related-issues-reply --> 🔍 **Related Issues Found** I found some existing issues that might be related. Please check if any of these are duplicates or contain helpful solutions: 1. 🟣 [#78](https://github.com/open-webui/computer/issues/78) **TypeError: expected string or bytes-like object, got 'list' when sending a message with an image attachment** *This is the closest prior report of the same underlying bug class: multimodal message content arriving as a list instead of a string and crashing a cptr message-path handler. The new issue simply identifies a different code location (`gateway.py`) that makes the same assumption.* *by chhotu-claw* 2. 🟣 [#153](https://github.com/open-webui/computer/issues/153) **bug: chat_messages INSERT crashes when Open WebUI `models` array reaches the scalar `model` column — message rows silently dropped** *Directly related because it is another chat persistence crash caused by binding a Python list into a scalar TEXT column during `chat_messages` INSERT. The new issue’s sqlite error is the same failure mode, just triggered by image-bearing `content` rather than `model`.* *by penradioGables* 3. 🟣 [#50](https://github.com/open-webui/computer/issues/50) **Gateway connection hangs in OpenWebUI** *Related at the symptom level because both describe OpenWebUI-to-cptr `/v1/chat/completions` requests that hang or never complete, indicating gateway-side failures in the same request path. However, this one appears broader and not specifically tied to multimodal attachments.* *by naqerl* --- 💡 If your issue is a duplicate, please close it and add any additional details to the existing issue instead. *This comment was generated automatically.* React with 👍 if helpful, 👎 if not.
Author
Owner

@tjbck commented on GitHub (Jul 31, 2026):

Addressed!

<!-- gh-comment-id:5147783549 --> @tjbck commented on GitHub (Jul 31, 2026): Addressed!
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-webui/computer#114
No description provided.