{"record":{"id":"3d61dd3e87aafb91","repo":"cloudflare/cloudflared","slug":"cloudflared-already-shutdown","errorCode":null,"errorMessage":"cloudflared already shutdown","messagePattern":"cloudflared already shutdown","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"orchestration/orchestrator.go","lineNumber":153,"sourceCode":"\tif maxActiveFlowsLocalConfig == \"\" {\n\t\treturn nil\n\t}\n\n\tmaxActiveFlowsLocalOverride, err := strconv.ParseUint(maxActiveFlowsLocalConfig, 10, 64)\n\tif err != nil {\n\t\treturn pkgerrors.Wrapf(err, \"failed to parse %s\", flags.MaxActiveFlows)\n\t}\n\n\t// Override the value that comes from the remote with the local value\n\tremoteWarpRouting.MaxActiveFlows = maxActiveFlowsLocalOverride\n\treturn nil\n}\n\n// The caller is responsible to make sure there is no concurrent access\nfunc (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting ingress.WarpRoutingConfig) error {\n\tselect {\n\tcase <-o.shutdownC:\n\t\treturn fmt.Errorf(\"cloudflared already shutdown\")\n\tdefault:\n\t}\n\n\t// Overrides the local values, onto the remote values of the warp routing configuration\n\tif err := o.overrideRemoteWarpRoutingWithLocalValues(&warpRouting); err != nil {\n\t\treturn pkgerrors.Wrap(err, \"failed to merge local overrides into warp routing configuration\")\n\t}\n\n\t// Assign the internal ingress rules to the parsed ingress\n\tingressRules.InternalRules = o.internalRules\n\n\t// Check if ingress rules are empty, and add the default route if so.\n\tif ingressRules.IsEmpty() {\n\t\tingressRules.Rules = ingress.GetDefaultIngressRules(o.log)\n\t}\n\n\t// Start new proxy before closing the ones from last version.\n\t// The upside is we don't need to restart proxy from last version, which can fail","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/orchestration/orchestrator.go#L135-L171","documentation":"Returned by Orchestrator.updateIngress when the orchestrator's shutdown channel has already been closed, meaning cloudflared has shut down and no further configuration updates can be applied. It is a lifecycle guard: applying new ingress/warp-routing rules after shutdown is invalid. Callers like UpdateConfig receive this when config reload races shutdown.","triggerScenarios":"Calling UpdateConfig (remote config change or SIGHUP-triggered reload) after the orchestrator/tunnel has been shut down — e.g. during process teardown, or a config file watcher firing while cloudflared is exiting.","commonSituations":"SIGTERM/SIGINT delivered while a remote configuration update is in flight; a file-watcher or management-host config push racing process exit; systemd restarting the service with a queued reload; tests that call NewOrchestrator, shut down, then UpdateConfig.","solutions":["Treat the error as benign during shutdown: log at debug/info and abort the update","Check tunnel shutdown state before pushing config updates (e.g. via management APIs that report process state)","Ensure shutdown completes before restart/reload logic issues new updates; serialize updates with the shutdown signal","If seen at startup unexpectedly, investigate an early shutdown (config error or signal) that closed shutdownC before the first update"],"exampleFix":"// before\nif err := orchestrator.UpdateConfig(config); err != nil {\n    return fmt.Errorf(\"update failed: %w\", err)\n}\n// after\nif err := orchestrator.UpdateConfig(config); err != nil {\n    if strings.Contains(err.Error(), \"already shutdown\") {\n        log.Debug().Msg(\"skipping config update: tunnel already shut down\")\n        return nil\n    }\n    return fmt.Errorf(\"update failed: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// Go: before pushing a config update, ensure the orchestrator is still running\nselect {\ncase <-shutdownC:\n    return errors.New(\"tunnel shutting down; skip config update\")\ndefault:\n}\nerr := orchestrator.UpdateConfig(config)","typeGuard":null,"tryCatchPattern":"err := orchestrator.UpdateConfig(cfg)\nif err != nil {\n    if strings.Contains(err.Error(), \"cloudflared already shutdown\") {\n        log.Debug().Msg(\"config update skipped: orchestrator already shut down\")\n        return nil\n    }\n    return err\n}","preventionTips":["Stop config reloads/watchers during graceful shutdown before closing shutdownC","Serialize UpdateConfig calls with lifecycle state; never update after Shutdown is initiated","In supervisors (systemd), avoid queuing SIGHUP reloads while the unit is stopping"],"tags":["go","lifecycle","shutdown","race"],"backgroundTag":"invalid-state-transition","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}