bytedance/deer-flow · error · HTTPException

Configuration not available

Error message

Configuration not available

What it means

Raised as HTTP 503 by the Gateway when the AppConfig cannot be materialised at request time. get_app_config() is invoked per-request so hot-reload of config.yaml takes effect; any failure to load or validate it (missing file, permission denied, YAML parse error, schema validation error) is wrapped into HTTPException(503, 'Configuration not available') with the original exception logged server-side.

Source

Thrown at backend/app/gateway/deps.py:362

    (engines, sandbox provider, IM channels, logging handler) require a
    process restart to change at runtime. The authoritative list lives in
    :mod:`deerflow.config.reload_boundary` and is mirrored by the
    standardised ``"startup-only:"`` prefix on the matching
    ``Field(description=...)`` in :class:`AppConfig` — IDE hover on those
    fields will surface the boundary inline. See
    ``backend/CLAUDE.md`` "Config Hot-Reload Boundary" for the operator
    summary.

    Any failure to materialise the config (missing file, permission denied,
    YAML parse error, validation error) is reported as 503 — semantically
    "the gateway cannot serve requests without a usable configuration" — and
    logged with the original exception so operators have something to debug.
    """
    try:
        return get_app_config()
    except Exception as exc:  # noqa: BLE001 - request boundary: log and degrade gracefully
        logger.exception("Failed to load AppConfig at request time")
        raise HTTPException(status_code=503, detail="Configuration not available") from exc


@asynccontextmanager
async def langgraph_runtime(app: FastAPI, startup_config: AppConfig) -> AsyncGenerator[None, None]:
    """Bootstrap and tear down all LangGraph runtime singletons.

    ``startup_config`` is the ``AppConfig`` snapshot taken once during
    ``lifespan()`` for one-shot infrastructure bootstrap. The engines and
    stores constructed here (stream bridge, persistence engine, checkpointer,
    store, run-event store) are restart-required by design — they hold live
    connections, file handles, or singleton providers — so they bind to this
    snapshot and survive across `config.yaml` edits. Request-time consumers
    must still go through :func:`get_config` for any field that should be
    hot-reloadable. See ``backend/CLAUDE.md`` "Config Hot-Reload Boundary".

    The matching ``run_events_config`` is frozen onto ``app.state`` so
    :func:`get_run_context` pairs a freshly-loaded ``AppConfig`` with the
    *startup-time* run-events configuration the underlying ``event_store``

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Check the Gateway logs: the line 'Failed to load AppConfig at request time' includes the original exception (FileNotFoundError, PermissionError, yaml.YAMLError, or ValidationError) which names the exact cause
  2. Validate config.yaml: run `python -c "from app.config import get_app_config; get_app_config()"` in backend/ or `make doctor` to surface the validation error directly
  3. Fix the YAML/schema issue named in the logged exception (restore the file, correct permissions, fix indentation/keys)
  4. If config.yaml is absent, copy config.example.yaml to config.yaml and extensions_config.example.json to extensions_config.json, then retry the request (no restart needed since config is read per-request)

Example fix

# config.yaml broken (e.g. tab indentation) -> 503 on every request
# fix: validate before the Gateway serves
# cd backend && python -c "from app.config import get_app_config; print(get_app_config())"
# -> ValidationError: 'models' must not be empty ... correct it, request succeeds without restart
Defensive patterns

Strategy: validation

Validate before calling

import yaml, pathlib
cfg = pathlib.Path('config.yaml')
assert cfg.exists(), 'config.yaml missing — run `make config`'
yaml.safe_load(cfg.read_text())  # raises on parse error before any HTTP call

Type guard

null

Try / catch

from fastapi import HTTPException
try:
    resp = client.get('/api/...')
except HTTPException as e:
    if e.status_code == 503 and e.detail == 'Configuration not available':
        # config-layer failure: surface ops message, do not retry with same config
        raise SystemExit('Gateway config unusable — check Gateway logs')
    raise

Prevention

When it happens

Trigger: Any Gateway REST API request made while config.yaml is missing, unreadable (bad permissions), contains invalid YAML syntax, or fails AppConfig schema validation. Also triggered if the process's working directory changed so the config path no longer resolves, or if config.yaml was edited mid-request into a broken state.

Common situations: Operator edits config.yaml live and saves a half-typed file (hot-reload picks up the broken snapshot); fresh clone without running `make config` to copy config.example.yaml to config.yaml; file ownership/permission mistakes when running the Gateway under a different user; YAML indentation or duplicate-key mistakes.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/f87d73024b3f543a. Report an issue: GitHub.