cloudflare/cloudflared · warning
cloudflared already shutdown
Error message
cloudflared already shutdown
What it means
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.
Source
Thrown at orchestration/orchestrator.go:153
if maxActiveFlowsLocalConfig == "" {
return nil
}
maxActiveFlowsLocalOverride, err := strconv.ParseUint(maxActiveFlowsLocalConfig, 10, 64)
if err != nil {
return pkgerrors.Wrapf(err, "failed to parse %s", flags.MaxActiveFlows)
}
// Override the value that comes from the remote with the local value
remoteWarpRouting.MaxActiveFlows = maxActiveFlowsLocalOverride
return nil
}
// The caller is responsible to make sure there is no concurrent access
func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting ingress.WarpRoutingConfig) error {
select {
case <-o.shutdownC:
return fmt.Errorf("cloudflared already shutdown")
default:
}
// Overrides the local values, onto the remote values of the warp routing configuration
if err := o.overrideRemoteWarpRoutingWithLocalValues(&warpRouting); err != nil {
return pkgerrors.Wrap(err, "failed to merge local overrides into warp routing configuration")
}
// Assign the internal ingress rules to the parsed ingress
ingressRules.InternalRules = o.internalRules
// Check if ingress rules are empty, and add the default route if so.
if ingressRules.IsEmpty() {
ingressRules.Rules = ingress.GetDefaultIngressRules(o.log)
}
// Start new proxy before closing the ones from last version.
// The upside is we don't need to restart proxy from last version, which can failView on GitHub (pinned to 2253eeeb25)
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
Example fix
// before
if err := orchestrator.UpdateConfig(config); err != nil {
return fmt.Errorf("update failed: %w", err)
}
// after
if err := orchestrator.UpdateConfig(config); err != nil {
if strings.Contains(err.Error(), "already shutdown") {
log.Debug().Msg("skipping config update: tunnel already shut down")
return nil
}
return fmt.Errorf("update failed: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: before pushing a config update, ensure the orchestrator is still running
select {
case <-shutdownC:
return errors.New("tunnel shutting down; skip config update")
default:
}
err := orchestrator.UpdateConfig(config) Try / catch
err := orchestrator.UpdateConfig(cfg)
if err != nil {
if strings.Contains(err.Error(), "cloudflared already shutdown") {
log.Debug().Msg("config update skipped: orchestrator already shut down")
return nil
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- ErrUnauthorized
- ErrBadRequest
- flow was closed directly
- write to closed websocket connection
- backend returned invalid network %s
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/3d61dd3e87aafb91.
Report an issue: GitHub.