{"record":{"id":"1d9e567455c64133","repo":"istio/istio","slug":"jwt-authenticator-is-nil","errorCode":null,"errorMessage":"JWT authenticator is nil","messagePattern":"JWT authenticator is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pilot/pkg/bootstrap/server.go","lineNumber":399,"sourceCode":"\n\t// This should be called only after controllers are initialized.\n\ts.initRegistryEventHandlers()\n\n\ts.initDiscoveryService()\n\n\t// Notice that the order of authenticators matters, since at runtime\n\t// authenticators are activated sequentially and the first successful attempt\n\t// is used as the authentication result.\n\tauthenticators := []security.Authenticator{\n\t\t&authenticate.ClientCertAuthenticator{},\n\t}\n\tif args.JwtRule != \"\" {\n\t\tjwtAuthn, err := initOIDC(args, s.environment.Watcher)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error initializing OIDC: %v\", err)\n\t\t}\n\t\tif jwtAuthn == nil {\n\t\t\treturn nil, fmt.Errorf(\"JWT authenticator is nil\")\n\t\t}\n\t\tauthenticators = append(authenticators, jwtAuthn)\n\t}\n\t// The k8s JWT authenticator requires the multicluster registry to be initialized,\n\t// so we build it later.\n\tif s.kubeClient != nil {\n\t\tauthenticators = append(authenticators,\n\t\t\tkubeauth.NewKubeJWTAuthenticator(\n\t\t\t\ts.environment.Watcher,\n\t\t\t\ts.kubeClient.Kube(),\n\t\t\t\ts.clusterID,\n\t\t\t\targs.RegistryOptions.KubeOptions.ClusterAliases,\n\t\t\t\ts.multiclusterController))\n\t}\n\tif len(features.TrustedGatewayCIDR) > 0 {\n\t\tauthenticators = append(authenticators, &authenticate.XfccAuthenticator{})\n\t}\n\tif features.XDSAuth {","sourceCodeStart":381,"sourceCodeEnd":417,"githubUrl":"https://github.com/istio/istio/blob/8dc789c5cf17517c64e3c36cb3288230f149dfae/pilot/pkg/bootstrap/server.go#L381-L417","documentation":"Defensive guard in server startup: initOIDC parsed the JWT rule and constructed an authenticator without error, yet NewJwtAuthenticator returned a nil value. This should never happen in current code (the constructor returns a non-nil authenticator or an error), so hitting it indicates an inconsistent build or a future refactor bug, not a user misconfiguration.","triggerScenarios":"A modified or older istiod build where authenticate.NewJwtAuthenticator can return (nil, nil); no runtime configuration path produces it.","commonSituations":"Custom forks of Istio that change NewJwtAuthenticator's contract; virtually unseen in upstream releases.","solutions":["Treat it as a code defect: check your Istio version / any patches to security/pkg/authentication/*.go.","Rebuild/upgrade to an unmodified release image and restart istiod.","Report upstream if reproducible with stock images."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// After calling initOIDC, guard against nil before use.\njwtAuthn, err := initOIDC(args, watcher)\nif err != nil { return err }\nif jwtAuthn == nil || jwtAuthn.Authenticate == nil {\n    return errors.New(\"JWT authenticator is nil\")\n}","tryCatchPattern":null,"preventionTips":["In forks, keep NewJwtAuthenticator's contract: never return (nil, nil).","Add a unit test asserting non-nil authenticator for a valid rule."],"tags":["istiod","jwt","internal","defensive-check"],"backgroundTag":null,"analyzedSha":"8dc789c5cf17517c64e3c36cb3288230f149dfae","analyzedAt":"2026-08-15T15:16:55.434Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}