{"record":{"id":"991c7ebe34a8f110","repo":"hashicorp/nomad","slug":"jwt-login-did-not-return-a-token","errorCode":null,"errorMessage":"JWT login did not return a token","messagePattern":"JWT login did not return a token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/vaultclient/vaultclient.go","lineNumber":165,"sourceCode":"\tif req.Namespace != \"\" {\n\t\tcc.SetNamespace(req.Namespace)\n\t}\n\n\tjwtLoginPath := fmt.Sprintf(\"auth/%s/login\", c.config.JWTAuthBackendPath)\n\ts, err := cc.Logical().WriteWithContext(ctx, jwtLoginPath,\n\t\tmap[string]any{\n\t\t\t\"role\": req.Role,\n\t\t\t\"jwt\":  req.JWT,\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn \"\", false, 0, fmt.Errorf(\"failed to login with JWT: %v\", err)\n\t}\n\tif s == nil {\n\t\treturn \"\", false, 0, errors.New(\"JWT login returned an empty secret\")\n\t}\n\tif s.Auth == nil {\n\t\treturn \"\", false, 0, errors.New(\"JWT login did not return a token\")\n\t}\n\n\tfor _, w := range s.Warnings {\n\t\tc.logger.Warn(\"JWT login warning\", \"warning\", w)\n\t}\n\n\treturn s.Auth.ClientToken, s.Auth.Renewable, s.Auth.LeaseDuration, nil\n}\n\nfunc (c *vaultClient) Renew(ctx context.Context, token string, lease int) (duration time.Duration, err error) {\n\tcc, err := c.Clone()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tcc.SetToken(token)\n\n\tres, err := cc.Auth().Token().RenewSelfWithContext(ctx, lease)\n\tif err != nil {","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/vaultclient/vaultclient.go#L147-L183","documentation":"Raised by DeriveTokenWithJWT in client/vaultclient/vaultclient.go when Vault returns a Secret but its Auth block is nil, meaning the login endpoint did not authenticate the caller or return a token. In Vault, a nil Auth on a login response typically indicates the request was accepted but no auth lease was created (e.g. an error swallowed by the server or a login endpoint that returned a non-auth response).","triggerScenarios":"JWT auth backend returning a 200 with no auth block (e.g. role misconfiguration, policies denying token creation); hitting the wrong login path so a generic endpoint responds instead of the JWT login; Vault token quotas or policy preventing token issuance while still returning a response object.","commonSituations":"Wrong JWTAuthBackendPath configured in Nomad; Vault JWT/OIDC role not bound to the presented JWT claims; Vault 'token' namespace or cluster issues where auth creation silently fails; expired/invalid JWTs in Vault versions that return 200 with empty auth.","solutions":["Verify the JWT and role are valid: run `vault write auth/<mount>/login role=<role> jwt=<token>` manually against Vault","Check the configured JWTAuthBackendPath matches the enabled auth mount in Vault","Inspect Vault audit logs for the login request to see why no token was issued","Check Vault token quota/policy settings that might suppress token creation","Upgrade Nomad/Vault pair; older Vault versions returned empty auth on some login failures"],"exampleFix":"// before\n// Vault returns secret with nil Auth; code proceeds and panics on s.Auth.ClientToken\ntoken := s.Auth.ClientToken\n// after\nif s == nil { return \"\", false, 0, errors.New(\"JWT login returned an empty secret\") }\nif s.Auth == nil { return \"\", false, 0, errors.New(\"JWT login did not return a token\") }\ntoken := s.Auth.ClientToken","handlingStrategy":"retry","validationCode":"// Pre-flight: verify JWT and role resolve to a token outside Nomad:\n// vault write auth/<mount>/login role=<role> jwt=<jwt>  -> must return auth.client_token","typeGuard":"func secretHasToken(s *vaultapi.Secret) bool {\n\treturn s != nil && s.Auth != nil && s.Auth.ClientToken != \"\"\n}","tryCatchPattern":"token, _, _, err := client.DeriveTokenWithJWT(ctx, req)\nif err != nil {\n\tif strings.Contains(err.Error(), \"JWT login did not return a token\") {\n\t\t// inspect role/mount config; refresh JWT; retry after fix\n\t}\n\treturn err\n}","preventionTips":["Keep the Vault JWT/OIDC role bound to the claims your workload actually presents","Confirm JWTAuthBackendPath matches the enabled Vault auth mount","Check Vault token quotas and policies before rollout so token creation cannot be silently suppressed"],"tags":["vault","jwt","auth","missing-token"],"backgroundTag":"vault-jwt-login-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}