{"record":{"id":"ae792ef249785f06","repo":"caddyserver/caddy","slug":"shutting-down-admin-server-v","errorCode":null,"errorMessage":"shutting down admin server: %v","messagePattern":"shutting down admin server: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"admin.go","lineNumber":750,"sourceCode":"\tif strings.HasSuffix(allowedPath, \"/\") {\n\t\treturn strings.HasPrefix(reqPath, allowedPath)\n\t}\n\treturn strings.HasPrefix(reqPath, allowedPath+\"/\")\n}\n\nfunc stopAdminServer(srv *http.Server) error {\n\tif srv == nil {\n\t\treturn fmt.Errorf(\"no admin server\")\n\t}\n\ttimeout := 10 * time.Second\n\tctx, cancel := context.WithTimeoutCause(context.Background(), timeout, fmt.Errorf(\"stopping admin server: %ds timeout\", int(timeout.Seconds())))\n\tdefer cancel()\n\terr := srv.Shutdown(ctx)\n\tif err != nil {\n\t\tif cause := context.Cause(ctx); cause != nil && errors.Is(err, context.DeadlineExceeded) {\n\t\t\terr = cause\n\t\t}\n\t\treturn fmt.Errorf(\"shutting down admin server: %v\", err)\n\t}\n\tLog().Named(\"admin\").Info(\"stopped previous server\", zap.String(\"address\", srv.Addr))\n\treturn nil\n}\n\n// AdminRouter is a type which can return routes for the admin API.\ntype AdminRouter interface {\n\tRoutes() []AdminRoute\n}\n\n// AdminRoute represents a route for the admin endpoint.\ntype AdminRoute struct {\n\tPattern string\n\tHandler AdminHandler\n}\n\ntype adminHandler struct {\n\tmux *http.ServeMux","sourceCodeStart":732,"sourceCodeEnd":768,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/admin.go#L732-L768","documentation":"Wrapper returned when http.Server.Shutdown of the admin endpoint fails for any reason (after substituting the timeout cause if applicable). The %v carries the underlying net/http error; the most common value is context deadline exceeded, i.e. the graceful stop didn't finish in 10 seconds.","triggerScenarios":"Config change forcing the old admin server to stop while a request or connection is still active past the 10s budget; shutdown erroring for OS-level reasons (listener already closed, FD issues).","commonSituations":"Hanging clients on the admin port; repeated rapid config loads; containers with aggressive health-check polling of the admin endpoint during reload.","solutions":["Inspect the wrapped error text — 'context deadline exceeded' means clients held connections: find and close them","Reduce admin endpoint traffic during config changes (pause scrapes/health checks during deploy)","If it persists, report upstream with the inner error and connection dump"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := changeConfig(http.MethodPost, \"/load\", newCfg, \"\", false); err != nil {\n    var apiErr caddy.APIError\n    if errors.As(err, &apiErr) && strings.Contains(apiErr.Error(), \"shutting down admin server\") {\n        time.Sleep(time.Second)\n        // one retry after old connections drain\n        err = changeConfig(http.MethodPost, \"/load\", newCfg, \"\", false)\n    }\n    if err != nil {\n        log.Fatal(err)\n    }\n}","preventionTips":["Find and close connections blocking admin shutdown (ss -tnp on the admin port)","Schedule config pushes when admin traffic is quiet","Treat timeout-flavored shutdown errors as transient; retry once before escalating"],"tags":["admin-api","shutdown","http","timeout"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}