bytedance/deer-flow · critical · RuntimeError
Failed to read JWT secret from {secret_file}. Set AUTH_JWT_S
Error message
Failed to read JWT secret from {secret_file}. Set AUTH_JWT_SECRET explicitly or fix DEER_FLOW_HOME/base directory permissions so DeerFlow can read its persisted auth secret. What it means
When no AUTH_JWT_SECRET env var is set, the Gateway derives its JWT signing secret from {base_dir}/.jwt_secret. If that file exists but reading it raises OSError (permission denied, SELinux/AppArmor denial, I/O error), this RuntimeError is raised, chaining the original exception. The two remedies in the message are: set the env var explicitly, or fix directory ownership/permissions under DEER_FLOW_HOME.
Source
Thrown at backend/app/gateway/auth/config.py:48
_auth_config: AuthConfig | None = None
def _load_or_create_secret() -> str:
"""Load persisted JWT secret from ``{base_dir}/.jwt_secret``, or generate and persist a new one."""
from deerflow.config.paths import get_paths
paths = get_paths()
secret_file = paths.base_dir / _SECRET_FILE
try:
if secret_file.exists():
secret = secret_file.read_text(encoding="utf-8").strip()
if secret:
return secret
except OSError as exc:
raise RuntimeError(f"Failed to read JWT secret from {secret_file}. Set AUTH_JWT_SECRET explicitly or fix DEER_FLOW_HOME/base directory permissions so DeerFlow can read its persisted auth secret.") from exc
secret = secrets.token_urlsafe(32)
try:
secret_file.parent.mkdir(parents=True, exist_ok=True)
fd = os.open(secret_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as fh:
fh.write(secret)
except OSError as exc:
raise RuntimeError(f"Failed to persist JWT secret to {secret_file}. Set AUTH_JWT_SECRET explicitly or fix DEER_FLOW_HOME/base directory permissions so DeerFlow can store a stable auth secret.") from exc
return secret
def get_auth_config() -> AuthConfig:
"""Get the global AuthConfig instance. Parses from env on first call."""
global _auth_config
if _auth_config is None:
from dotenv import load_dotenv
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Fix ownership/permissions: `chown -R <gateway-user> <DEER_FLOW_HOME>` and ensure the data dir is readable (e.g. 0700 dir, 0600 file owned by the service user).
- Alternatively set AUTH_JWT_SECRET in the environment to a stable value and skip the file path entirely.
- If on NFS/SELinux, verify the mount/options permit the service user's reads.
- Note: an empty-but-readable file falls through to regeneration (a different error, 79, covers write failure).
Example fix
# before: data dir owned by root, gateway runs as deerflow docker compose up # -> Failed to read JWT secret ... # after sudo chown -R 1000:1000 ./deer-flow-data docker compose up
Defensive patterns
Strategy: try-catch
Validate before calling
from pathlib import Path
def jwt_secret_readable(base_dir: Path) -> bool:
f = base_dir / '.jwt_secret'
if not f.exists():
return True # nothing to read yet
try:
return bool(f.read_text(encoding='utf-8').strip())
except OSError:
return False Try / catch
try:
secret = load_jwt_secret()
except RuntimeError as e:
if 'Failed to read JWT secret' in str(e):
secret = os.environ['AUTH_JWT_SECRET'] # documented escape hatch
else:
raise Prevention
- Run the Gateway as a dedicated user that owns DEER_FLOW_HOME from first boot.
- Set AUTH_JWT_SECRET explicitly in containerized/orchestrated deployments.
- Smoke-test secret readability in the container entrypoint before exec'ing the app.
When it happens
Trigger: The Gateway process (often containerized, running as a non-root user) cannot read .jwt_secret because the file or an ancestor directory is owned by root with restrictive modes; DEER_FLOW_HOME points at a read-only or NFS-mounted volume with broken permissions; security modules blocking the read.
Common situations: Docker bind-mount of the data dir created by root on the host but the container runs as another UID; moving a data directory between hosts with `sudo cp` losing ownership; read-only mounts in hardened deployments.
Related errors
- Failed to persist JWT secret to {secret_file}. Set AUTH_JWT_
- Permission denied: {resource}:{action}
- Token error: {payload.value}
- USER_NOT_FOUND
- TOKEN_INVALID
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/bee1901a487874c5.
Report an issue: GitHub.