{"record":{"id":"222dd4c632fe7670","repo":"gofiber/fiber","slug":"fiber-service-q-is-nil","errorCode":null,"errorMessage":"fiber: service %q is nil","messagePattern":"fiber: service %q is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services.go","lineNumber":124,"sourceCode":"\t\t}\n\n\t\terrs = append(errs, fmt.Errorf(\"service %s start: %w\", srv.String(), err))\n\t}\n\treturn errors.Join(errs...)\n}\n\n// shutdownServices Handles the shutdown process of services for the current application.\n// Iterates over all the started services in reverse order and tries to terminate them,\n// returning an error if any error occurs.\nfunc (app *App) shutdownServices(ctx context.Context) error {\n\tif app.state.ServicesLen() == 0 {\n\t\treturn nil\n\t}\n\n\tvar errs []error\n\tfor key, srv := range app.state.Services() {\n\t\tif srv == nil {\n\t\t\treturn fmt.Errorf(\"fiber: service %q is nil\", key)\n\t\t}\n\t\tif err := ctx.Err(); err != nil {\n\t\t\t// Context is canceled, do a best effort to terminate the services.\n\t\t\terrs = append(errs, fmt.Errorf(\"service %s terminate: %w\", srv.String(), err))\n\t\t\tcontinue\n\t\t}\n\n\t\terr := srv.Terminate(ctx)\n\t\tif err != nil {\n\t\t\t// Best effort to terminate the services.\n\t\t\terrs = append(errs, fmt.Errorf(\"service %s terminate: %w\", srv.String(), err))\n\t\t\tcontinue\n\t\t}\n\n\t\t// Remove the service from the State\n\t\tapp.state.deleteService(srv)\n\t}\n\treturn errors.Join(errs...)","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/services.go#L106-L142","documentation":"Returned by shutdownServices() when iterating app.state.Services() (the set of successfully started services) and a value is nil. app.state should only contain non-nil services (set via app.state.setService after a successful Start), so a nil here indicates internal corruption or a custom State mutation.","triggerScenarios":"Something wrote a nil into the State's service map after Start completed — e.g., a custom addon or test harness calling an unexported setter, or a race on shutdown. Under the normal Listen/Shutdown flow this is unreachable because setService only runs on successful Start.","commonSituations":"Test code that manipulates app.state directly; a fork that adds custom State mutation; memory corruption. Practically a defensive guard that should never fire in production.","solutions":["Do not write to app.state outside the framework's start/terminate lifecycle.","If writing tests, use the public Services() read path and avoid injecting nils.","Treat an occurrence as a corruption signal and audit any addon/middleware that touches State."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Never write to State outside the framework; if you must, validate.\nfunc stateHealthy(app *fiber.App) error {\n    for k, s := range app.State().Services() {\n        if s == nil { return fmt.Errorf(\"nil service in state at %q\", k) }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat app.state as framework-private.","Avoid test code that injects nils into State."],"tags":["services","shutdown","state","nil-guard","defensive"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}