hashicorp/nomad · error

failed to serialize client_assertion jwt: %w

Error message

failed to serialize client_assertion jwt: %w

What it means

Thrown by oidcClientAssertion when j.Serialize() fails while rendering the built client_assertion JWT (only when config.VerboseLogging is enabled, for a debug log). Serialize re-signs/encodes the JWT; failure means the JWS cannot be produced even though the JWT object was constructed — usually key/algorithm incompatibility surfacing at signing time.

Source

Thrown at nomad/acl_endpoint.go:3181

	// KeySource = "nomad", but we get it here to avoid exposing more of the
	// codebase to the encrypter.
	nomadKey, nomadKID, err := a.srv.encrypter.GetActiveKey()
	if err != nil {
		return nil, fmt.Errorf("failed to get active nomad key: %w", err)
	}
	j, err := oidc.BuildClientAssertionJWT(config, nomadKey, nomadKID)
	if err != nil {
		return nil, fmt.Errorf("failed to build client_assertion jwt: %w", err)
	}
	if config.VerboseLogging {
		// a user initially setting up the auth method, as one might with
		// VerboseLogging enabled, may benefit from not having to do a full
		// login flow to see the jwt (and any possible Serialize() error).
		// we say "example" in the log, because the cap library will run
		// Serialize() again internally, so it won't use this same jwt.
		token, err := j.Serialize()
		if err != nil {
			return nil, fmt.Errorf("failed to serialize client_assertion jwt: %w", err)
		}
		a.logger.Debug("example client_assertion", "oidc_client_id", config.OIDCClientID, "jwt", token)
	}
	return j, nil
}

func (a *ACL) CreateClientIntroductionToken(
	args *structs.ACLCreateClientIntroductionTokenRequest,
	reply *structs.ACLCreateClientIntroductionTokenResponse) error {

	authErr := a.srv.Authenticate(a.ctx, args)

	if done, err := a.srv.forward(structs.ACLCreateClientIntroductionTokenRPCMethod, args, args, reply); done {
		return err
	}
	a.srv.MeasureRPCRate("acl", structs.RateMetricWrite, args)

	// This endpoint can only be used once all servers in the local region have

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped cause; fix the key/algorithm mismatch in OIDCClientAssertion (SigningAlgorithm vs actual key type).
  2. Verify the private key PEM (KeySource=private-key) is valid and unencrypted, or that the nomad keyring key is healthy.
  3. Disable VerboseLogging to unblock the flow if it only fails on this debug path, then report the underlying serialization issue upstream.
  4. Upgrade Nomad / cap library if serialization is failing on a valid configuration.

Example fix

// before: verbose logging with mismatched algorithm
"OIDCClientAssertion": { "KeySource": "nomad", "SigningAlgorithm": "ES256" }, "VerboseLogging": true
// after: algorithm that matches available keys
"OIDCClientAssertion": { "KeySource": "nomad", "SigningAlgorithm": "RS256" }, "VerboseLogging": true
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: same key/algorithm validation as build step, plus verbose-logging awareness
if cfg.VerboseLogging && cfg.OIDCClientAssertion.KeySource == "private-key" {
  if _, err := jwtSignedParser(cfg.OIDCClientAssertion.PrivateKey, cfg.OIDCClientAssertion.SigningAlgorithm); err != nil {
    return err
  }
}

Try / catch

token, err := j.Serialize()
if err != nil {
    return nil, fmt.Errorf("failed to serialize client_assertion jwt: %w", err)
}

Prevention

When it happens

Trigger: config.VerboseLogging is true and j.Serialize() errors on the example JWT inside oidcClientAssertion, which is called by UpsertAuthMethods and oidcRequest.

Common situations: Same key/algorithm mismatch as jwt build errors but detected at serialization, corrupted key material, or a cap library serialization bug — only visible to users who enable VerboseLogging on the auth method.

Related errors


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