{"record":{"id":"270a8a4169a4eccf","repo":"bytedance/deer-flow","slug":"gateway-workers-workers-cannot-run-with-schedule","errorCode":null,"errorMessage":"GATEWAY_WORKERS={workers} cannot run with scheduler.enabled=true because each worker starts its own scheduler. Set GATEWAY_WORKERS=1, scheduler.multi_instance=true, or scheduler.enabled=false.","messagePattern":"GATEWAY_WORKERS=(.+?) cannot run with scheduler\\.enabled=true because each worker starts its own scheduler\\. Set GATEWAY_WORKERS=1, scheduler\\.multi_instance=true, or scheduler\\.enabled=false\\.","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"critical","filePath":"backend/app/gateway/deps.py","lineNumber":106,"sourceCode":"    multi_instance_requested = bool(getattr(scheduler, \"multi_instance\", False))\n    multi_instance_scheduler = bool(getattr(scheduler, \"enabled\", False) and multi_instance_requested)\n\n    backend = getattr(config.database, \"backend\", None)\n    run_events_backend = getattr(getattr(config, \"run_events\", None), \"backend\", None)\n    run_ownership = getattr(config, \"run_ownership\", None)\n\n    if multi_instance_requested and backend != \"postgres\":\n        raise SystemExit(f\"scheduler.multi_instance=true requires database.backend='postgres'. database.backend is '{backend}'. Set scheduler.multi_instance=false or configure Postgres.\")\n    if multi_instance_requested and run_events_backend != \"db\":\n        raise SystemExit(f\"scheduler.multi_instance=true requires run_events.backend='db'. run_events.backend is '{run_events_backend}'. Set scheduler.multi_instance=false or configure run_events.backend: db.\")\n    if multi_instance_requested and (run_ownership is None or not run_ownership.heartbeat_enabled):\n        raise SystemExit(\"scheduler.multi_instance=true requires run_ownership.heartbeat_enabled=true so peer runs retain a valid lease. Set scheduler.multi_instance=false or enable run ownership heartbeats.\")\n\n    if workers <= 1:\n        return\n\n    if config.scheduler.enabled and not multi_instance_scheduler:\n        raise SystemExit(f\"GATEWAY_WORKERS={workers} cannot run with scheduler.enabled=true because each worker starts its own scheduler. Set GATEWAY_WORKERS=1, scheduler.multi_instance=true, or scheduler.enabled=false.\")\n\n    if _browser_tools_enabled_in_config(config):\n        raise SystemExit(browser_multi_worker_error(workers))\n\n    if backend != \"postgres\":\n        raise SystemExit(f\"GATEWAY_WORKERS={workers} requires database.backend='postgres', but database.backend is '{backend}'. SQLite cannot support concurrent multi-process access. Set GATEWAY_WORKERS=1 or switch to Postgres.\")\n\n    if run_events_backend != \"db\":\n        raise SystemExit(\n            f\"GATEWAY_WORKERS={workers} requires run_events.backend='db', but run_events.backend is '{run_events_backend}'. \"\n            \"Memory and JSONL event stores are process-local, so delivery receipt singleton guarantees cannot hold across workers. \"\n            \"Set GATEWAY_WORKERS=1 or configure run_events.backend: db.\"\n        )\n\n    if run_ownership is None or not run_ownership.heartbeat_enabled:\n        raise SystemExit(\n            f\"GATEWAY_WORKERS={workers} requires run_ownership.heartbeat_enabled=true. \"\n            \"Without heartbeat, every run has a NULL lease, so reconciliation \"","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/deps.py#L88-L124","documentation":"SystemExit raised at startup when GATEWAY_WORKERS>1, scheduler.enabled=true, and scheduler.multi_instance is false (multi_instance_scheduler = enabled AND multi_instance). Each uvicorn worker would start its own scheduler, causing every cron/interval task to fire N times. The gate demands either a single worker, explicit multi-instance coordination, or no scheduler.","triggerScenarios":"Launching with GATEWAY_WORKERS=2 or more (env var) while config.yaml has scheduler.enabled: true and multi_instance unset/false. Only reached when workers>1 (the workers<=1 branch returns earlier).","commonSituations":"Raising worker count for throughput on a box that also runs scheduled tasks; production Docker image defaults with GATEWAY_WORKERS set in compose; forgetting that the scheduler is enabled from an earlier single-worker setup.","solutions":["Set GATEWAY_WORKERS=1","Or set scheduler.multi_instance: true (plus the postgres/run_events/heartbeat prerequisites it gates on)","Or set scheduler.enabled: false if scheduled tasks run elsewhere"],"exampleFix":"# environment\n# before\nGATEWAY_WORKERS=4\n# after (option A)\nGATEWAY_WORKERS=1\n# after (option B, in config.yaml)\nscheduler:\n  enabled: true\n  multi_instance: true","handlingStrategy":"validation","validationCode":"import os\ncfg = load_config()\nworkers = int(os.environ.get(\"GATEWAY_WORKERS\", \"1\"))\nsched = getattr(cfg, \"scheduler\", None)\nmi = bool(getattr(sched, \"multi_instance\", False))\nassert not (workers > 1 and sched and sched.enabled and not mi), \"workers>1 + scheduler needs multi_instance\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default GATEWAY_WORKERS=1 in deploy manifests unless multi_instance is fully configured","Change worker counts deliberately, with the scheduler flags reviewed in the same change","Make 'process fails at boot with SystemExit' a monitored deploy failure, not a silent restart loop"],"tags":["config","startup","scheduler","workers","scaling","system-exit","gateway"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}