[GH-ISSUE #22965] issue: Code interpreter prompt (Pyodide) injected into user turn instead of system prompt #35383

Closed
opened 2026-04-25 09:35:46 -05:00 by GiteaMirror · 11 comments
Owner

Originally created by @AirRunner on GitHub (Mar 24, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/22965

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
  • I am using the latest version of Open WebUI.

Installation Method

Pip Install

Open WebUI Version

v0.8.10

Ollama Version (if applicable)

No response

Operating System

macOS Tahoe 26.3.1

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

The Pyodide environment instructions (CODE_INTERPRETER_PYODIDE_PROMPT) should be injected into the system prompt, so the model treats them as execution context, not as something the user said.

Actual Behavior

The Pyodide environment instructions are appended to the last user message via add_or_update_user_message(). As a result, the model treats them as user-provided content.

For any message, the context sent to the model looks like this:

<|im_start|>user
What is the capital of France?

##### Pyodide Environment
- This Python environment runs via Pyodide in the browser. **Do not install packages** — `pip install`, `subprocess`, and `micropip.install()` are not available.
- If a required library is unavailable, use an alternative approach with available modules. Do not attempt to install anything.

##### Persistent File System
- User-uploaded files are available at `/mnt/uploads/`. When the user asks you to work with their files, read from this directory.
...
<|im_end|>

This causes the model to:

  • Acknowledge "thanks for the info about Pyodide" unprompted
  • Reference /mnt/uploads/ as if it were a real path on the user's machine
  • Treat every message as if the user is discussing a Python execution environment

Steps to Reproduce

  1. Install Open WebUI v0.8.10 via pip on Python 3.12: pip install open-webui
  2. Start the server: open-webui serve
  3. In the chat interface: Integrations → enable Code Interpreter
  4. Start a new chat with any LLM
  5. Send a simple message: "Hello!"
  6. Observe the model's response. It might acknowledge Pyodide context despite the user never mentioning it
  7. Inspect the raw payload sent to the LLM to confirm the Pyodide instructions appear inside the user role message rather than the system role message

Logs & Screenshots

Image

Raw tokenized context captured from the model input (ChatML format):

<|im_start|>user
Hello!

##### Pyodide Environment
- This Python environment runs via Pyodide in the browser. **Do not install packages** — `pip install`, `subprocess`, and `micropip.install()` are not available.
- If a required library is unavailable, use an alternative approach with available modules. Do not attempt to install anything.

##### Persistent File System
- User-uploaded files are available at `/mnt/uploads/`. When the user asks you to work with their files, read from this directory.
- You can also write output files to `/mnt/uploads/` so the user can access and download them from the file browser.
- The file system persists across code executions within the same session.
- Use `import os; os.listdir('/mnt/uploads')` to discover available files.
<|im_end|>
<|im_start|>assistant
<think>

Additional Information

Root cause

In backend/open_webui/utils/middleware.py, the code interpreter prompt is injected using add_or_update_user_message():

form_data["messages"] = add_or_update_user_message(
    prompt,  # DEFAULT_CODE_INTERPRETER_PROMPT + CODE_INTERPRETER_PYODIDE_PROMPT
    form_data["messages"],
)

add_or_update_user_message() appends content to the last user message in the list (see utils/misc.py), placing execution environment instructions inside the user role (where the model expects human input).

Inconsistency with RAG

The RAG context injection in the same file already handles this correctly with a configurable toggle:

if RAG_SYSTEM_CONTEXT:
    form_data["messages"] = add_or_update_system_message(rag_content, messages, append=True)
else:
    form_data["messages"] = add_or_update_user_message(rag_content, messages, append=False)

The code interpreter has no equivalent option and is hardcoded to the user turn.

Proposed fix

In backend/open_webui/utils/middleware.py, replace the add_or_update_user_message call with:

form_data["messages"] = add_or_update_system_message(
    prompt,
    form_data["messages"],
    append=True,
)

This appends the Pyodide instructions to the existing system prompt without modifying the user message. Optionally, a CODE_INTERPRETER_SYSTEM_CONTEXT env var (mirroring RAG_SYSTEM_CONTEXT) could be added for flexibility.

Originally created by @AirRunner on GitHub (Mar 24, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/22965 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!). - [x] I am using the latest version of Open WebUI. ### Installation Method Pip Install ### Open WebUI Version v0.8.10 ### Ollama Version (if applicable) _No response_ ### Operating System macOS Tahoe 26.3.1 ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior The Pyodide environment instructions (`CODE_INTERPRETER_PYODIDE_PROMPT`) should be injected into the **system prompt**, so the model treats them as execution context, not as something the user said. ### Actual Behavior The Pyodide environment instructions are appended to the **last user message** via `add_or_update_user_message()`. As a result, the model treats them as user-provided content. For any message, the context sent to the model looks like this: ``` <|im_start|>user What is the capital of France? ##### Pyodide Environment - This Python environment runs via Pyodide in the browser. **Do not install packages** — `pip install`, `subprocess`, and `micropip.install()` are not available. - If a required library is unavailable, use an alternative approach with available modules. Do not attempt to install anything. ##### Persistent File System - User-uploaded files are available at `/mnt/uploads/`. When the user asks you to work with their files, read from this directory. ... <|im_end|> ``` This causes the model to: - Acknowledge "thanks for the info about Pyodide" unprompted - Reference `/mnt/uploads/` as if it were a real path on the user's machine - Treat every message as if the user is discussing a Python execution environment ### Steps to Reproduce 1. Install Open WebUI v0.8.10 via pip on Python 3.12: `pip install open-webui` 2. Start the server: `open-webui serve` 3. In the chat interface: Integrations → enable **Code Interpreter** 4. Start a new chat with any LLM 5. Send a simple message: `"Hello!"` 6. Observe the model's response. It might acknowledge Pyodide context despite the user never mentioning it 7. Inspect the raw payload sent to the LLM to confirm the Pyodide instructions appear inside the `user` role message rather than the `system` role message ### Logs & Screenshots <img width="1052" height="554" alt="Image" src="https://github.com/user-attachments/assets/14025de0-6a7e-4f6c-b638-97760e010149" /> --- Raw tokenized context captured from the model input (ChatML format): ``` <|im_start|>user Hello! ##### Pyodide Environment - This Python environment runs via Pyodide in the browser. **Do not install packages** — `pip install`, `subprocess`, and `micropip.install()` are not available. - If a required library is unavailable, use an alternative approach with available modules. Do not attempt to install anything. ##### Persistent File System - User-uploaded files are available at `/mnt/uploads/`. When the user asks you to work with their files, read from this directory. - You can also write output files to `/mnt/uploads/` so the user can access and download them from the file browser. - The file system persists across code executions within the same session. - Use `import os; os.listdir('/mnt/uploads')` to discover available files. <|im_end|> <|im_start|>assistant <think> ``` ### Additional Information ### Root cause In `backend/open_webui/utils/middleware.py`, the code interpreter prompt is injected using `add_or_update_user_message()`: ```python form_data["messages"] = add_or_update_user_message( prompt, # DEFAULT_CODE_INTERPRETER_PROMPT + CODE_INTERPRETER_PYODIDE_PROMPT form_data["messages"], ) ``` `add_or_update_user_message()` appends `content` to the last user message in the list (see `utils/misc.py`), placing execution environment instructions inside the `user` role (where the model expects human input). ### Inconsistency with RAG The RAG context injection in the same file already handles this correctly with a configurable toggle: ```python if RAG_SYSTEM_CONTEXT: form_data["messages"] = add_or_update_system_message(rag_content, messages, append=True) else: form_data["messages"] = add_or_update_user_message(rag_content, messages, append=False) ``` The code interpreter has no equivalent option and is hardcoded to the user turn. ### Proposed fix In `backend/open_webui/utils/middleware.py`, replace the `add_or_update_user_message` call with: ```python form_data["messages"] = add_or_update_system_message( prompt, form_data["messages"], append=True, ) ``` This appends the Pyodide instructions to the existing system prompt without modifying the user message. Optionally, a `CODE_INTERPRETER_SYSTEM_CONTEXT` env var (mirroring `RAG_SYSTEM_CONTEXT`) could be added for flexibility.
GiteaMirror added the bug label 2026-04-25 09:35:46 -05:00
Author
Owner

@BillionClaw commented on GitHub (Mar 24, 2026):

Investigating this prompt injection issue with Pyodide — submitting a fix.

<!-- gh-comment-id:4115064546 --> @BillionClaw commented on GitHub (Mar 24, 2026): Investigating this prompt injection issue with Pyodide — submitting a fix.
Author
Owner

@BillionClaw commented on GitHub (Mar 24, 2026):

Investigating this prompt injection issue with Pyodide — submitting a fix.

<!-- gh-comment-id:4115746955 --> @BillionClaw commented on GitHub (Mar 24, 2026): Investigating this prompt injection issue with Pyodide — submitting a fix.
Author
Owner

@tjbck commented on GitHub (Mar 24, 2026):

Intended.

<!-- gh-comment-id:4116811129 --> @tjbck commented on GitHub (Mar 24, 2026): Intended.
Author
Owner

@siffreinsg commented on GitHub (Mar 24, 2026):

Intended.

Hi, could you provide more context on why this is intended please?
The current implementation appears hacky, and introduce problems such as models focusing on the Pyodide environment instead of the user query, especially on smaller models.

As far as I could understand from the code, there is no way to stop this behaviour. Would it be possible to provide an environment variable to toggle the prompt appending or change the Pyodide instructions?

<!-- gh-comment-id:4118791106 --> @siffreinsg commented on GitHub (Mar 24, 2026): > Intended. Hi, could you provide more context on why this is intended please? The current implementation appears hacky, and introduce problems such as models focusing on the Pyodide environment instead of the user query, especially on smaller models. As far as I could understand from the code, there is no way to stop this behaviour. Would it be possible to provide an environment variable to toggle the prompt appending or change the Pyodide instructions?
Author
Owner

@AirRunner commented on GitHub (Mar 24, 2026):

Hi @tjbck, thanks for looking at this. I just wanted to follow up on your response, because I'm genuinely confused about the intent here.

A few things I'm having trouble reconciling:

Inconsistency with RAG injection

The RAG context injection in the same file (middleware.py) already has a RAG_SYSTEM_CONTEXT toggle that switches between add_or_update_system_message and add_or_update_user_message, precisely because the two placements have different semantics. The code interpreter has no equivalent and is hardcoded to the user turn. If there's a principled reason to keep it in the user turn, shouldn't RAG default to the same behavior?

The injected content isn't user-authored

The Pyodide prompt describes the execution environment. It's platform context, not something the user said. Placing it in the user role means the model sees it as human input, which can cause models to fixate on it or treat it as a user request rather than a constraint.

The proposed compromise preserves current behavior

I suggested a CODE_INTERPRETER_SYSTEM_CONTEXT env var (mirroring RAG_SYSTEM_CONTEXT), defaulting to False to keep the existing behavior intact. That way nothing changes for anyone who doesn't opt in.


So anyway, I'm not asking to reopen the issue if the intent is clear, I just can't find the scenario where injecting execution environment instructions into the user turn is preferable. Could you share a bit more context on the reasoning?

Thanks!

<!-- gh-comment-id:4121999037 --> @AirRunner commented on GitHub (Mar 24, 2026): Hi @tjbck, thanks for looking at this. I just wanted to follow up on your response, because I'm genuinely confused about the intent here. A few things I'm having trouble reconciling: ## Inconsistency with RAG injection The RAG context injection in the same file (`middleware.py`) already has a `RAG_SYSTEM_CONTEXT` toggle that switches between `add_or_update_system_message` and `add_or_update_user_message`, precisely because the two placements have different semantics. The code interpreter has no equivalent and is hardcoded to the user turn. If there's a principled reason to keep it in the user turn, shouldn't RAG default to the same behavior? ## The injected content isn't user-authored The Pyodide prompt describes the execution environment. It's platform context, not something the user said. Placing it in the `user` role means the model sees it as human input, which can cause models to fixate on it or treat it as a user request rather than a constraint. ## The proposed compromise preserves current behavior I suggested a `CODE_INTERPRETER_SYSTEM_CONTEXT` env var (mirroring `RAG_SYSTEM_CONTEXT`), defaulting to `False` to keep the existing behavior intact. That way nothing changes for anyone who doesn't opt in. --- So anyway, I'm not asking to reopen the issue if the intent is clear, I just can't find the scenario where injecting execution environment instructions into the user turn is preferable. Could you share a bit more context on the reasoning? Thanks!
Author
Owner

@oryschakj-personal commented on GitHub (Mar 26, 2026):

For reference, this is the kind of thing you see in responses due to this issue:

"(Also, please stop pasting that Pyodide environment info. It's your system template and I've mentioned this before. I'll just ignore it going forward.)"

It's not being ignored, it's brought up consistently.

<!-- gh-comment-id:4136913044 --> @oryschakj-personal commented on GitHub (Mar 26, 2026): For reference, this is the kind of thing you see in responses due to this issue: "(Also, please stop pasting that Pyodide environment info. It's your system template and I've mentioned this before. I'll just ignore it going forward.)" It's not being ignored, it's brought up consistently.
Author
Owner

@arkham000 commented on GitHub (Mar 28, 2026):

I'm having the same problem, I'm on macos OS 26.3, I'm using oMLX 0.2.23 and openwebui 0.8.12, I'm using qwen3.5 35b and it seems to receive some Pyodide blocks, but it seems smart enough to ignore it because it stays focused on my the subject and it only adds :
"The block of text you see next ("Pyodide Environment...") is intrusive and appears to come from a code execution interface (perhaps a note or file you opened by mistake), it has nothing to do with Docker. We can ignore it completely."

<!-- gh-comment-id:4148090314 --> @arkham000 commented on GitHub (Mar 28, 2026): I'm having the same problem, I'm on macos OS 26.3, I'm using oMLX 0.2.23 and openwebui 0.8.12, I'm using qwen3.5 35b and it seems to receive some Pyodide blocks, but it seems smart enough to ignore it because it stays focused on my the subject and it only adds : "The block of text you see next ("Pyodide Environment...") is intrusive and appears to come from a code execution interface (perhaps a note or file you opened by mistake), it has nothing to do with Docker. We can ignore it completely."
Author
Owner

@onscreen commented on GitHub (Apr 4, 2026):

Also having same issue with multiple models, occasionally the models will tell me I keep pasting details about the pyodide environment because of this, I too am confused why this is intended.

<!-- gh-comment-id:4187600253 --> @onscreen commented on GitHub (Apr 4, 2026): Also having same issue with multiple models, occasionally the models will tell me I keep pasting details about the pyodide environment because of this, I too am confused why this is intended.
Author
Owner

@AirRunner commented on GitHub (Apr 4, 2026):

Update

@siffreinsg @oryschakj-personal @arkham000 @onscreen

This has been addressed in 7c52382c on the dev branch.

The fix is conditional on native tool calling mode, which is actually the case where it matters most for cache invalidation (#23269) and arguably also for model behavior.

So in native FC, CODE_INTERPRETER_PYODIDE_PROMPT is now injected into the system prompt.


In hindsight, the intent from tjbck probably referred to non-native mode.

The fix will certainly land in a next release!

<!-- gh-comment-id:4187823052 --> @AirRunner commented on GitHub (Apr 4, 2026): ## Update @siffreinsg @oryschakj-personal @arkham000 @onscreen This has been addressed in [`7c52382c`](https://github.com/open-webui/open-webui/commit/7c52382c) on the dev branch. The fix is conditional on **native tool calling mode**, which is actually the case where it matters most for cache invalidation (#23269) and arguably also for model behavior. So in **native FC**, `CODE_INTERPRETER_PYODIDE_PROMPT` is now injected into the system prompt. --- In hindsight, the intent from tjbck probably referred to non-native mode. The fix will certainly land in a next release!
Author
Owner

@Master-Pr0grammer commented on GitHub (Apr 8, 2026):

Very happy to see this being fixed.

Would it not make more sense to just simply put these arbitrary instructions in the tool instructions itself?

Inserting it into the system prompt would cause a worse hit on the prompt cache when the tool is enabled/disabled, causing full prompt reprocessing even though it is not necessary.

<!-- gh-comment-id:4208019048 --> @Master-Pr0grammer commented on GitHub (Apr 8, 2026): Very happy to see this being fixed. Would it not make more sense to just simply put these arbitrary instructions in the tool instructions itself? Inserting it into the system prompt would cause a worse hit on the prompt cache when the tool is enabled/disabled, causing full prompt reprocessing even though it is not necessary.
Author
Owner

@MrCrin commented on GitHub (Apr 11, 2026):

I'm also having problems with this, particularly when I'm trying to debug things. The model gets confused and thinks the reference to the pyodide file is part of the context I'm providing around the problem. Glad to hear this is being fixed. Do we know which release it's likely to land in?

<!-- gh-comment-id:4228337980 --> @MrCrin commented on GitHub (Apr 11, 2026): I'm also having problems with this, particularly when I'm trying to debug things. The model gets confused and thinks the reference to the pyodide file is part of the context I'm providing around the problem. Glad to hear this is being fixed. Do we know which release it's likely to land in?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#35383