hashicorp/nomad · error

failed to login with JWT: %v

Error message

failed to login with JWT: %v

What it means

DeriveTokenWithJWT wraps any error returned by Vault's JWT auth login (client.auth.JWT().Login) with this message. It means the Vault login call itself failed — the underlying error carries the real cause (network, 400/403, unknown role, etc.). The function derives a workload token by exchanging a JWT for a Vault secret.

Source

Thrown at client/vaultclient/vaultclient.go:159

		return "", false, 0, err
	}

	// 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 {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped %v cause to identify the underlying failure (network vs auth rejection).
  2. Verify the Vault JWT auth role name in the request matches a role configured on the Vault auth method.
  3. Check that the JWT is valid and unexpired (decode it or test with 'vault write auth/jwt/login role=... jwt=...').
  4. Verify Vault address, TLS, and namespace settings in the Nomad Vault config.
  5. Confirm the JWT auth method is enabled at the expected mount path in Vault.

Example fix

// before
role := "old-role" // removed in Vault
// after
role := "nomad-workloads" // role that exists on the Vault jwt auth method
Defensive patterns

Strategy: try-catch

Validate before calling

// before deriving: ensure inputs are sane
if req.JWT == "" || req.Role == "" {
    return fmt.Errorf("JWT and role are required before Vault login")
}

Type guard

func hasVaultAuth(s *api.Secret) bool { return s != nil && s.Auth != nil && s.Auth.ClientToken != "" }

Try / catch

token, _, _, err := vc.DeriveTokenWithJWT(req)
if err != nil {
    if strings.Contains(err.Error(), "failed to login with JWT") {
        logger.Error("vault jwt login failed", "cause", err, "role", req.Role)
        // check role name, JWT expiry, Vault reachability before retrying
    }
    return err
}

Prevention

When it happens

Trigger: Calling DeriveTokenWithJWT when the Vault server is unreachable, the auth method/role 'req.Role' does not exist or mismatches the JWT, the JWT is expired/invalid, or the mount path is wrong — any non-nil error from the login call.

Common situations: Vault role renamed or deleted after agent config was written; expired workload JWT; Vault agent/auth mount disabled; TLS or network misconfiguration between Nomad client and Vault; wrong Vault address/namespace in config.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/0280eba16079d837. Report an issue: GitHub.