Is there an existing issue for the same bug?
Bug Description
When Agent Canvas uses the Docker conversation runtime, a configured
title_llm_profile is passed into the conversation, but the inner Agent Server
cannot resolve it because its per-conversation LLM profile store is empty.
The outer Agent Canvas / Agent Server has a valid LLM profile named
LLMStudioAux, configured as the default/title-generation profile.
The Docker conversation runtime starts with:
OH_PERSISTENCE_DIR=/var/openhands/.openhands
and its profile directory is:
/var/openhands/.openhands/profiles
but that directory contains only:
No LLM profile JSON files are propagated into the runtime.
As a result, automatic title generation logs that LLMStudioAux cannot be
found and falls back to another LLM path. For an ACP Codex conversation this
then produces an unrelated LiteLLM/OpenAI authentication error because the
ACP agent itself is not a directly callable LiteLLM model.
The underlying coding agent continues to work; only the auxiliary title-
generation path is broken.
Expected Behavior
A Docker-backed conversation should be able to resolve the LLM profile selected
for title generation by Agent Canvas.
If title_llm_profile="LLMStudioAux" is passed when creating the conversation,
the inner Agent Server should have enough runtime configuration to load that
profile.
The solution should not require exposing the entire host OpenHands persistence
directory to the conversation container.
Actual Behavior
The inner Agent Server receives the title LLM profile name but cannot resolve it.
Inside the conversation container:
docker exec <conversation-container> sh -lc '
echo "OH_PERSISTENCE_DIR=$OH_PERSISTENCE_DIR"
ls -la /var/openhands/.openhands/profiles
'
produces:
OH_PERSISTENCE_DIR=/var/openhands/.openhands
total 0
drwxr-xr-x ...
drwx------ ...
-rw-r--r-- ... .profiles.lock
The runtime then logs that the configured title profile LLMStudioAux does not
exist and that there are no available profiles.
The model endpoint itself is reachable from the same container:
docker exec <conversation-container> \
curl -s http://host.docker.internal:1234/v1/models
and returns, among others:
Therefore this is not an LM Studio networking failure. The missing piece is
the LLM profile definition inside the per-conversation runtime.
Steps to Reproduce
-
Run Agent Canvas with Docker conversation runtimes:
OH_CONVERSATION_RUNTIME=docker \
OH_CONVERSATION_IMAGE=ghcr.io/openhands/agent-server:1.49.5-python \
agent-canvas
-
Create an LLM profile in Agent Canvas, for example:
name: LLMStudioAux
model: openai/qwen/qwen3.6-35b-a3b
base URL: http://127.0.0.1:1234/v1
-
Configure Agent Canvas to use LLMStudioAux for conversation title generation.
-
Start a new ACP/Codex conversation.
-
The outer Agent Server passes the title profile name into the conversation.
-
Inspect the conversation container:
docker exec <container> \
ls -la /var/openhands/.openhands/profiles
-
Observe that the directory contains only .profiles.lock.
-
Observe the inner Agent Server log reporting that LLMStudioAux cannot be
found / that no LLM profiles are available.
-
Verify separately that the container can reach the local model using:
curl http://host.docker.internal:1234/v1/models
which succeeds.
Acceptance Criteria
Installation Method
Agent Canvas with Docker conversation runtime on macOS.
If you selected "Other", please specify
No response
SDK Version
openhands-agent-server / openhands-sdk 1.49.5 Agent Canvas 1.22.0
Version Confirmation
Python Version
No response
Model Name (if applicable)
openai/qwen/qwen3.6-35b-a3b via LM Studio
Operating System
MacOS
Logs and Error Messages
Failed to load title LLM profile 'LLMStudioAux':
Profile LLMStudioAux not found.
Available profiles: none.
Falling back to the agent's LLM.
Minimal Code Sample
No response
Screenshots and Additional Context
The fallback subsequently produced a LiteLLM/OpenAI invalid_api_key error,
but that is secondary. The primary failure is that the Docker runtime's
LLM profile store is empty.
This appears related to the title-generation/profile work tracked in #4199,
which was completed before the current per-conversation Docker runtime topology.
The configured profile exists in the outer Agent Canvas environment, and the
inner container can reach the model endpoint successfully via
host.docker.internal:1234.
The failure is specifically that the profile reference crosses the runtime
boundary, but the profile definition does not.
OpenHands AI triage
The following comments and acceptance criteria were added by the OpenHands AI agent.
Triage
This is a real, narrowly scoped bug in the per-conversation Docker conversation runtime. The outer agent-server resolves title_llm_profile only as a name: AutoTitleSubscriber._load_title_llm calls get_llm_profile_store().load(profile_name, ...), and in Docker mode that store resolves under the container's OH_PERSISTENCE_DIR (/var/openhands/.openhands/profiles), which is the bind-mounted per-conversation runtime persistence dir and is seeded with nothing but .profiles.lock. title_llm_profile is carried across the boundary verbatim (docker_runtime/mediation.py::prepare_start/serialize_start), unlike agent_profile_id, which is resolved outer-side before serialization. So the profile definition never reaches the inner runtime.
Smallest coherent scope: at Docker container provisioning (docker_runtime/registry.py::_build_container), seed the per-conversation runtime profile store with the profile named by title_llm_profile (and only that profile), encrypted with the conversation's per-runtime key, so the existing inner LLMProfileStore loads it with no new wire field or endpoint. Reuse the authoritative LLMProfileStore / get_llm_profile_store() mechanism and the existing pydantic_secrets/Cipher handling; do not add a parallel store or redaction path. The public title_llm_profile request-contract semantics and the existing fallback precedence (title_llm_profile → agent.llm → truncation) stay unchanged.
Explicit non-goals:
- Do not change title-generation precedence or fallback behavior for non-Docker/local runtimes.
- Do not bind-mount the host OpenHands persistence directory into the conversation container, and do not copy unrelated host profiles.
- Do not add a new REST endpoint or a new title-generation API surface.
- The secondary LiteLLM
invalid_api_key symptom is out of scope; the defect is the empty inner profile store.
Acceptance Criteria
Is there an existing issue for the same bug?
Bug Description
When Agent Canvas uses the Docker conversation runtime, a configured
title_llm_profile is passed into the conversation, but the inner Agent Server
cannot resolve it because its per-conversation LLM profile store is empty.
The outer Agent Canvas / Agent Server has a valid LLM profile named
LLMStudioAux, configured as the default/title-generation profile.The Docker conversation runtime starts with:
and its profile directory is:
but that directory contains only:
No LLM profile JSON files are propagated into the runtime.
As a result, automatic title generation logs that
LLMStudioAuxcannot befound and falls back to another LLM path. For an ACP Codex conversation this
then produces an unrelated LiteLLM/OpenAI authentication error because the
ACP agent itself is not a directly callable LiteLLM model.
The underlying coding agent continues to work; only the auxiliary title-
generation path is broken.
Expected Behavior
A Docker-backed conversation should be able to resolve the LLM profile selected
for title generation by Agent Canvas.
If
title_llm_profile="LLMStudioAux"is passed when creating the conversation,the inner Agent Server should have enough runtime configuration to load that
profile.
The solution should not require exposing the entire host OpenHands persistence
directory to the conversation container.
Actual Behavior
The inner Agent Server receives the title LLM profile name but cannot resolve it.
Inside the conversation container:
produces:
The runtime then logs that the configured title profile
LLMStudioAuxdoes notexist and that there are no available profiles.
The model endpoint itself is reachable from the same container:
and returns, among others:
Therefore this is not an LM Studio networking failure. The missing piece is
the LLM profile definition inside the per-conversation runtime.
Steps to Reproduce
Run Agent Canvas with Docker conversation runtimes:
Create an LLM profile in Agent Canvas, for example:
Configure Agent Canvas to use
LLMStudioAuxfor conversation title generation.Start a new ACP/Codex conversation.
The outer Agent Server passes the title profile name into the conversation.
Inspect the conversation container:
Observe that the directory contains only
.profiles.lock.Observe the inner Agent Server log reporting that
LLMStudioAuxcannot befound / that no LLM profiles are available.
Verify separately that the container can reach the local model using:
which succeeds.
Acceptance Criteria
title_llm_profile.Installation Method
Agent Canvas with Docker conversation runtime on macOS.
If you selected "Other", please specify
No response
SDK Version
openhands-agent-server / openhands-sdk 1.49.5 Agent Canvas 1.22.0
Version Confirmation
Python Version
No response
Model Name (if applicable)
openai/qwen/qwen3.6-35b-a3b via LM Studio
Operating System
MacOS
Logs and Error Messages
Failed to load title LLM profile 'LLMStudioAux':
Profile
LLMStudioAuxnot found.Available profiles: none.
Falling back to the agent's LLM.
Minimal Code Sample
No response
Screenshots and Additional Context
The fallback subsequently produced a LiteLLM/OpenAI invalid_api_key error,
but that is secondary. The primary failure is that the Docker runtime's
LLM profile store is empty.
This appears related to the title-generation/profile work tracked in #4199,
which was completed before the current per-conversation Docker runtime topology.
The configured profile exists in the outer Agent Canvas environment, and the
inner container can reach the model endpoint successfully via
host.docker.internal:1234.The failure is specifically that the profile reference crosses the runtime
boundary, but the profile definition does not.
OpenHands AI triage
The following comments and acceptance criteria were added by the OpenHands AI agent.
Triage
This is a real, narrowly scoped bug in the per-conversation Docker conversation runtime. The outer agent-server resolves
title_llm_profileonly as a name:AutoTitleSubscriber._load_title_llmcallsget_llm_profile_store().load(profile_name, ...), and in Docker mode that store resolves under the container'sOH_PERSISTENCE_DIR(/var/openhands/.openhands/profiles), which is the bind-mounted per-conversation runtime persistence dir and is seeded with nothing but.profiles.lock.title_llm_profileis carried across the boundary verbatim (docker_runtime/mediation.py::prepare_start/serialize_start), unlikeagent_profile_id, which is resolved outer-side before serialization. So the profile definition never reaches the inner runtime.Smallest coherent scope: at Docker container provisioning (
docker_runtime/registry.py::_build_container), seed the per-conversation runtime profile store with the profile named bytitle_llm_profile(and only that profile), encrypted with the conversation's per-runtime key, so the existing innerLLMProfileStoreloads it with no new wire field or endpoint. Reuse the authoritativeLLMProfileStore/get_llm_profile_store()mechanism and the existingpydantic_secrets/Cipherhandling; do not add a parallel store or redaction path. The publictitle_llm_profilerequest-contract semantics and the existing fallback precedence (title_llm_profile→agent.llm→ truncation) stay unchanged.Explicit non-goals:
invalid_api_keysymptom is out of scope; the defect is the empty inner profile store.Acceptance Criteria
OH_CONVERSATION_RUNTIME=docker, creating a conversation whosetitle_llm_profilenames an existing profile causes the inner agent-server to resolve that profile (noProfile <name> not found. Available profiles: nonelog entry), and the auto-generated conversation title is produced by that profile's LLM.<OH_PERSISTENCE_DIR>/profilesinside the container contains the referenced profile's JSON file and no other host profile files; the container mounts remain limited to the per-conversation conversation, persistence, and workspace directories (the host OpenHands persistence directory is not mounted).OH_SECRET_KEYand is decryptable by the innerLLMProfileStore; no plaintext key material is written to the runtime persistence directory.title_llm_profilegenerates its title via the auxiliary profile and does not fall back to the ACP agent's LLM or emit a LiteLLMinvalid_api_keyerror.title_llm_profilenames a missing or unloadable profile, the inner server logs the existing warning and falls back to the agent's LLM (then truncation) without failing conversation creation.tests/agent_server/docker_runtime/covers the Docker conversation runtime with a configuredtitle_llm_profile, asserting the profile is made available to the inner runtime and is used for title generation.