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

  1. Open the model picker in the chat UI and explicitly select a model, then send again
  2. Verify the endpoint has fetched its model list (check the endpoint's cached models in Settings)
  3. 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

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


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/dd2dd46560557b74. Report an issue: GitHub.