hashicorp/nomad · error
JWT login returned an empty secret
Error message
JWT login returned an empty secret
What it means
Raised by DeriveTokenWithJWT in client/vaultclient/vaultclient.go when the Vault JWT auth login (Logical().WriteWithContext on auth/<backend>/login) succeeds without an error but returns a nil/empty Secret. Vault normally always returns a secret on a successful login, so an empty one indicates an unexpected or malformed server response. The client guards against it to avoid a nil-pointer dereference on s.Auth.
Source
Thrown at client/vaultclient/vaultclient.go:162
// Make sure the login request is not passing any token and that we're using
// the expected namespace to login
cc.SetToken("")
if req.Namespace != "" {
cc.SetNamespace(req.Namespace)
}
jwtLoginPath := fmt.Sprintf("auth/%s/login", c.config.JWTAuthBackendPath)
s, err := cc.Logical().WriteWithContext(ctx, jwtLoginPath,
map[string]any{
"role": req.Role,
"jwt": req.JWT,
},
)
if err != nil {
return "", false, 0, fmt.Errorf("failed to login with JWT: %v", err)
}
if s == nil {
return "", false, 0, errors.New("JWT login returned an empty secret")
}
if s.Auth == nil {
return "", false, 0, errors.New("JWT login did not return a token")
}
for _, w := range s.Warnings {
c.logger.Warn("JWT login warning", "warning", w)
}
return s.Auth.ClientToken, s.Auth.Renewable, s.Auth.LeaseDuration, nil
}
func (c *vaultClient) Renew(ctx context.Context, token string, lease int) (duration time.Duration, err error) {
cc, err := c.Clone()
if err != nil {
return 0, err
}
cc.SetToken(token)View on GitHub (pinned to 482b49bf1a)
Solutions
- Check Vault server logs for the login request and confirm a normal 200 response with a secret body
- Inspect any proxy/load balancer between Nomad and Vault for response rewriting or empty-body interception
- Verify the jwt auth backend path (JWTAuthBackendPath) points to a real, enabled auth mount
- Retry the login; if it persists, capture a client trace of the raw Vault response and report to Vault/Nomad
Example fix
// before
s, err := cc.Logical().WriteWithContext(ctx, jwtLoginPath, data)
if s.Auth == nil { ... } // panics if s is nil
// after
s, err := cc.Logical().WriteWithContext(ctx, jwtLoginPath, data)
if err != nil { return "", false, 0, err }
if s == nil { return "", false, 0, errors.New("JWT login returned an empty secret") } Defensive patterns
Strategy: retry
Validate before calling
// Nothing to pre-validate client-side; ensure Vault address and JWT auth mount are configured:
if c.config.JWTAuthBackendPath == "" {
return fmt.Errorf("vault JWT auth backend path not configured")
} Type guard
func hasAuthSecret(s *vaultapi.Secret) bool {
return s != nil && s.Auth != nil && s.Auth.ClientToken != ""
} Try / catch
token, renewable, lease, err := client.DeriveTokenWithJWT(ctx, req)
if err != nil {
if strings.Contains(err.Error(), "JWT login returned an empty secret") {
// check Vault health/proxy, then retry with backoff
}
return err
} Prevention
- Keep no proxy/LB between Nomad and Vault that can rewrite or drop response bodies
- Verify the auth mount responds with a full secret by testing `vault write auth/<mount>/login ...` directly
- Monitor Vault health endpoints before issuing JWT logins
When it happens
Trigger: Vault's auth/<mount>/login endpoint returns HTTP 204 or an empty body; a proxy/load-balancer strips the response; a misbehaving Vault dev server or snapshot returns an empty secret payload for a successful request.
Common situations: Reverse proxies or service meshes (Envoy, Consul mesh) intercepting Vault responses; Vault cluster behind a misconfigured load balancer health-check rewriting responses; JWT auth backend misconfiguration returning empty auth blocks in unusual mounts.
Related errors
- no signed workload identity available
- JWT login did not return a token
- failed to login with JWT: %v
- [✘] Could not retrieve JWT accessor: %w
- [✘] Could not enable JWT credential backend: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/4e4714d0e80a5b69.
Report an issue: GitHub.