{"record":{"id":"c717a60686dae795","repo":"bytedance/deer-flow","slug":"failed-to-persist-jwt-secret-to-secret-file-set","errorCode":null,"errorMessage":"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.","messagePattern":"Failed to persist JWT secret to (.+?)\\. Set AUTH_JWT_SECRET explicitly or fix DEER_FLOW_HOME/base directory permissions so DeerFlow can store a stable auth secret\\.","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"backend/app/gateway/auth/config.py","lineNumber":57,"sourceCode":"    paths = get_paths()\n    secret_file = paths.base_dir / _SECRET_FILE\n\n    try:\n        if secret_file.exists():\n            secret = secret_file.read_text(encoding=\"utf-8\").strip()\n            if secret:\n                return secret\n    except OSError as exc:\n        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\n\n    secret = secrets.token_urlsafe(32)\n    try:\n        secret_file.parent.mkdir(parents=True, exist_ok=True)\n        fd = os.open(secret_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)\n        with os.fdopen(fd, \"w\", encoding=\"utf-8\") as fh:\n            fh.write(secret)\n    except OSError as exc:\n        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\n    return secret\n\n\ndef get_auth_config() -> AuthConfig:\n    \"\"\"Get the global AuthConfig instance. Parses from env on first call.\"\"\"\n    global _auth_config\n    if _auth_config is None:\n        from dotenv import load_dotenv\n\n        load_dotenv()\n        jwt_secret = os.environ.get(\"AUTH_JWT_SECRET\")\n        if not jwt_secret:\n            jwt_secret = _load_or_create_secret()\n            os.environ[\"AUTH_JWT_SECRET\"] = jwt_secret\n            logger.warning(\n                \"⚠ AUTH_JWT_SECRET is not set — using an auto-generated secret \"\n                \"persisted to .jwt_secret. Sessions will survive restarts. \"\n                \"For production, add AUTH_JWT_SECRET to your .env file: \"","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/auth/config.py#L39-L75","documentation":"The complement of the read error: no persisted secret was found (or it was empty), so the Gateway generated a fresh secrets.token_urlsafe(32) and tries to persist it to {base_dir}/.jwt_secret with 0o600 via a low-level os.open. If mkdir of the parent, file creation, or the write raises OSError, this RuntimeError is raised. Failing here matters because without persistence every restart mints a new secret and invalidates all issued JWTs.","triggerScenarios":"base_dir does not exist and cannot be created (read-only filesystem, missing parent, permission denied on DEER_FLOW_HOME); the directory is writable for mkdir but the file create/write is denied (immutable flag, disk full, quota exceeded); container running with a read-only volume for the config dir.","commonSituations":"Container image where the data path is read-only and no writable volume is mounted; disk-full conditions; a misconfigured DEER_FLOW_HOME pointing into the package install tree owned by root.","solutions":["Ensure the Gateway user can create and write files under DEER_FLOW_HOME/base_dir: `chown`/`chmod 700` the directory, mount a writable volume in Docker.","Point DEER_FLOW_HOME at a writable location (per-host persistent path) so the secret survives restarts.","As a fallback, set AUTH_JWT_SECRET env var explicitly so persistence is not required.","Check disk space and filesystem mount flags (ro) if permissions look correct."],"exampleFix":"# before: read-only mount\ndocker run ... -v /opt/deerflow:/deerflow:ro deerflow\n\n# after: writable persistent volume\ndocker run ... -v /opt/deerflow:/deerflow deerflow","handlingStrategy":"validation","validationCode":"import os, tempfile\n\ndef base_dir_writable(base_dir: Path) -> bool:\n    try:\n        base_dir.mkdir(parents=True, exist_ok=True)\n        with tempfile.TemporaryFile(dir=base_dir):\n            pass\n        return True\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    secret = load_or_persist_jwt_secret()\nexcept RuntimeError as e:\n    if 'Failed to persist JWT secret' in str(e):\n        logger.warning('falling back to env secret; sessions reset on restart')\n        secret = os.environ['AUTH_JWT_SECRET']\n    else:\n        raise","preventionTips":["Mount a writable persistent volume at DEER_FLOW_HOME in Docker/K8s.","Prefer an explicit AUTH_JWT_SECRET from a secret manager for multi-replica deployments — file persistence is single-host only.","Check disk space and read-only mount flags in deployment checklists."],"tags":["auth","jwt","permissions","deployment","persistence"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}