odysseus-dev/odysseus · error · HTTPException
No model selected for this chat. Open the model picker and c
Error message
No model selected for this chat. Open the model picker and choose one before sending.
What it means
After orphaned-endpoint cleanup and the Issue #587 recovery attempt (which tries to repopulate the model from the endpoint's cached model list), the session still has an empty model string. The handler rejects with 400 rather than calling the upstream with model="", which would only surface as a confusing 401/503.
Source
Thrown at routes/chat_routes.py:707
# Verify the caller owns this session before loading it.
# Without this, any authenticated user can post into another user's chat.
_verify_session_owner(request, session)
try:
sess = session_manager.get_session(session)
except KeyError:
raise HTTPException(404, f"Session '{session}' not found")
owner = effective_user(request)
if _clear_orphaned_session_endpoint(sess, owner=owner):
raise HTTPException(400, "Selected model endpoint was removed. Pick another model in Settings.")
# Empty model + live endpoint = setup race (Issue #587). Repair from
# the endpoint's cached model list before privilege checks, which
# otherwise see "" and behave inconsistently with the allowlist.
_recover_empty_session_model(sess, session, owner=owner)
if not getattr(sess, "model", "").strip():
raise HTTPException(
400,
"No model selected for this chat. Open the model picker and choose one before sending.",
)
if not (getattr(sess, "endpoint_url", "") or "").strip():
raise HTTPException(400, "Selected model endpoint is not configured")
# Same allowed_models + daily-cap gate as chat_stream (mirror so the
# non-streaming path can't be used to bypass).
_enforce_chat_privileges(request, sess)
tool_policy = build_effective_tool_policy(last_user_message=message)
allow_tool_preprocessing = not tool_policy.block_all_tool_calls
# Inline memory command
memory_response = None
if not tool_policy.blocks("manage_memory"):
memory_response = await chat_handler.handle_memory_command(sess, message)
if memory_response:View on GitHub (pinned to f9235ebbf1)
Solutions
- Open the model picker in the chat UI and explicitly select a model, then send again
- Verify the endpoint has fetched its model list (check the endpoint's cached models in Settings)
- For API callers, include a valid model when creating the session rather than leaving it blank
Defensive patterns
Strategy: validation
Validate before calling
if (!session.model || !session.model.trim()) {
const models = await fetchEndpointModels(session.endpoint_id);
if (models.length) await setSessionModel(session.id, models[0]);
else { promptModelPick(); return; }
} Type guard
function hasModel(s: {model?: string | null}): boolean {
return typeof s.model === 'string' && s.model.trim().length > 0;
} Prevention
- Block send until the session row actually has a model persisted
- After adding an endpoint, wait for model discovery before enabling chat
When it happens
Trigger: First-send race after endpoint setup where the picker showed a cached model but it was never persisted to the session row; endpoint's model cache is itself empty so recovery has nothing to pull; endpoint was deleted and re-created leaving the session modelless.
Common situations: User creates a chat immediately after adding an endpoint before models are fetched; a DB write failure when the model was chosen; sessions created via API without a model field.
Related errors
- Selected model endpoint was removed. Pick another model in S
- Selected model endpoint is not configured
- HTTP ${res.status}${body ? ': ' + body.slice(0, 160) : ''}
- Admin only
- Invalid SSH remote host
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/dd2dd46560557b74.
Report an issue: GitHub.