bytedance/deer-flow · error · HTTPException
Model {model_name!r} is not in the configured model allowlis
Error message
Model {model_name!r} is not in the configured model allowlist What it means
HTTP 400 raised when the caller supplies context.model_name and the Gateway cannot resolve it via app_config.get_model_config(model_name). The Gateway enforces a model allowlist at request admission: any model_name not present in config.yaml's models list is rejected before the run starts, even though the lead agent itself would silently fall back to the default model.
Source
Thrown at backend/app/gateway/services.py:1097
stream_modes = normalize_stream_modes(body.stream_mode)
bridge = get_stream_bridge(request)
run_mgr = get_run_manager(request)
run_ctx = get_run_context(request)
disconnect = DisconnectMode.cancel if body.on_disconnect == "cancel" else DisconnectMode.continue_
body_context = getattr(body, "context", None) or {}
model_name = body_context.get("model_name")
# Coerce non-string model_name values to str before truncation.
if model_name is not None and not isinstance(model_name, str):
model_name = str(model_name)
# Validate model against the allowlist when a model_name is provided.
if model_name:
app_config = get_app_config()
resolved = app_config.get_model_config(model_name)
if resolved is None:
raise HTTPException(
status_code=400,
detail=f"Model {model_name!r} is not in the configured model allowlist",
)
owner_user_id = get_trusted_internal_owner_user_id(request)
# Stateless run endpoints carry thread_id in the request *body*, so the
# @require_permission(owner_check=True) decorator -- which resolves ownership
# from the path param -- cannot protect them. Enforce thread ownership here,
# before any run is created, so one user cannot start runs on (or read /wait
# checkpoint state from) another user's thread. Missing rows (auto-created
# temp threads) and NULL-owner rows (shared / pre-auth data) stay accessible
# via check_access; only a thread already owned by another user is rejected
# with 404, matching thread_runs.py's anti-enumeration behaviour. Internal
# channel runs act on behalf of the connection owner carried in
# X-DeerFlow-Owner-User-Id, so they are scoped to that owner instead of
# bypassing the check -- a leaked internal token must not grant cross-user
# thread access.
user = getattr(request.state, "user", None)View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Compare the sent model_name against GET /api/models (or config.yaml models:) and use an exact configured name.
- Fix typos/whitespace/case in the model_name value the client sends.
- If the model should exist, add its full entry to config.yaml under models and restart the Gateway.
- Omit context.model_name entirely to let the server use its default model.
Example fix
# config.yaml
models:
- name: gpt-4o # client must send exactly "gpt-4o"
provider: openai
... Defensive patterns
Strategy: validation
Validate before calling
const models = await api.listModels(); // names from config.yaml
const allowed = new Set(models.map(m => m.name));
if (body.context?.model_name && !allowed.has(body.context.model_name)) {
delete body.context.model_name; // or pick allowed names interactively
} Type guard
const isConfiguredModel = (name, configured) => typeof name === 'string' && configured.some(m => m.name === name);
Try / catch
try { await api.createRun(threadId, body); } catch (e) { if (e.status === 400 && /allowlist/.test(e.detail)) { refresh model list, correct model_name, retry once; } throw e; } Prevention
- Drive the UI model picker from the server's model list, never a hardcoded list.
- Treat model names as exact-match opaque ids (case/whitespace sensitive).
- Restart Gateway after config.yaml model changes so all replicas agree.
When it happens
Trigger: POST a run with body.context.model_name set to a name that is not a key in config.yaml models (typo, removed model, env-specific name), or a non-string value coerced to str (e.g. 42 -> '42').
Common situations: Frontend hardcodes a model name from another environment (dev vs prod config.yaml differ); a model was renamed or deleted from config.yaml; the user picked a model in the UI that the backend config never defined; typo like 'gpt-4o ' with whitespace.
Related errors
- Failed to load MCP configuration
- Failed to update MCP configuration
- Failed to update MCP server
- Failed to load suggestions config: ${response.statusText}
- Thread {thread_id} not found
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/927523e34c69c449.
Report an issue: GitHub.