unclecode/crawl4ai · error · HTTPException
Token issuance is disabled: no api_token is configured on th
Error message
Token issuance is disabled: no api_token is configured on the server.
What it means
An explicit 403 raised by POST /token when the server has no security.api_token configured. Token issuance fails closed: previously the endpoint minted a JWT to anyone whose email domain merely had an MX record, so in the current design the absence of a configured api_token disables issuance entirely rather than falling back to weak verification.
Source
Thrown at deploy/docker/server.py:540
@app.exception_handler(Exception)
async def _unhandled_exception_handler(request: Request, exc: Exception):
cid = _uuid.uuid4().hex[:12]
logger.exception("unhandled exception [cid=%s]", cid)
return JSONResponse(
{"error": "Internal server error", "correlation_id": cid},
status_code=500,
)
# ──────────────────────── Endpoints ──────────────────────────
@app.post("/token")
async def get_token(req: TokenRequest):
expected_token = config.get("security", {}).get("api_token", "")
if not expected_token:
# Fail closed: without a configured api_token the old behavior minted a
# JWT to anyone whose email merely had an MX record. Refuse instead.
raise HTTPException(
403,
"Token issuance is disabled: no api_token is configured on the server.",
)
if not req.api_token or not constant_time_eq(req.api_token, expected_token):
raise HTTPException(401, "Invalid or missing api_token")
if not verify_email_domain(req.email):
raise HTTPException(400, "Invalid email domain")
token = create_access_token({"sub": req.email})
return {"email": req.email, "access_token": token, "token_type": "bearer"}
@app.post("/config/dump")
async def config_dump(
data: dict,
_td: Dict = Depends(token_dep),
):
try:
return JSONResponse(_config_from_json(data))View on GitHub (pinned to 7e80152142)
Solutions
- Configure security.api_token in the server config (via its config file or the secret-injection mechanism the deployment uses), then restart/reload the server.
- Verify with a config-health check or by confirming the secret is mounted before calling /token.
- If email-domain-based open issuance was intended, that behavior is intentionally removed - use the configured api_token flow.
Example fix
# before
# server config (yaml/json) lacks:
# security: {}
post(f"{base}/token", json={"email": "a@b.com", "api_token": ""}) # 403
# after
# server config:
# security: {"api_token": "<shared-secret>"}
post(f"{base}/token", json={"email": "a@b.com", "api_token": "<shared-secret>"}) Defensive patterns
Strategy: validation
Validate before calling
def token_issuance_available(base) -> bool:
"""Probe whether the server has an api_token configured.
403 from /token means issuance is disabled."""
r = post(f"{base}/token", json={'email': 'probe@example.com', 'api_token': 'x'})
return r.status_code != 403 Try / catch
try:
tok = post(f"{base}/token", json={'email': email, 'api_token': secret})
except HTTPError as e:
if e.response.status_code == 403:
raise RuntimeError(
'server has no security.api_token configured; ask the operator to set it'
) from e
raise Prevention
- Configure security.api_token as part of deployment provisioning, not as an afterthought.
- Add a startup/smoke check that fails deployment when /token returns 403 in environments that need JWTs.
- Treat 403 from /token as a configuration defect to fix server-side - no client workaround exists.
When it happens
Trigger: Calling POST /token with any credentials on a server whose config lacks the security.api_token key (fresh install, default config, or a deployment that intended JWT-only auth without shared-secret bootstrapping).
Common situations: New deployments that never set the api_token secret; environments where the token was expected to be injected via env/secret store but was not (secret mount missing); staging servers sharing a config template that omits the security block.
Related errors
- Invalid or missing api_token
- type must be 'CrawlerRunConfig' or 'BrowserConfig'
- URL must start with {schemes}
- Timeout after {timeout}ms waiting for selector '{css_selecto
- Invalid CSS selector: '{css_selector}'
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/87a978f50a8ef2f0.
Report an issue: GitHub.