hashicorp/nomad · error

failed to retrieve signed workload identity

Error message

failed to retrieve signed workload identity: %w

What it means

When the allocrunner's vault hook uses workload identity (JWT-based Vault auth) via deriveVaultTokenJWT, it requests a signed workload identity from the alloc runner's identity RPC. If the RPC fails, the error is wrapped and marked recoverable so the taskrunner can retry token derivation.

Solutions

  1. Retry — the error is already marked recoverable, so transient failures should resolve on retry
  2. Verify Nomad server version supports signed workload identities for tasks
  3. Check server config for vault/workload identity settings (jwt validation, signing keys)
  4. Inspect server logs for the identity-signing RPC failure to find the root cause
Defensive patterns

Strategy: retry

Validate before calling

// precheck server support before enabling WI-based vault
// ensure server version >= feature introduction and vault WI stanza configured

Try / catch

token, err := deriveVaultToken(ctx)
if err != nil {
    var rec *structs.RecoverableError
    if errors.As(err, &rec) && rec.IsRecoverable() {
        return retryWithBackoff(ctx, deriveVaultToken)
    }
    return err
}

Prevention

When it happens

Trigger: deriveVaultToken calls deriveVaultTokenJWT, which invokes the signed-identity getter with the task's wid name; the underlying client RPC (consul/vault identity signing endpoint) returns an error — server unreachable, signing not enabled, or transient backend failure.

Common situations: Nomad server that does not yet support workload-identity signing (version mismatch); Vault/Consul integration misconfigured on the server; transient network partitions between client and server during allocation start.

Related errors


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

Appendix: source

Thrown at client/allocrunner/taskrunner/vault_hook.go:377

		select {
		case <-ctx.Done():
			return "", 0, ctx.Err()
		case <-timer.C:
		}
	}
}

// deriveVaultTokenJWT returns a Vault ACL token using JWT auth login.
func (h *vaultHook) deriveVaultTokenJWT(ctx context.Context) (string, int, error) {
	// Retrieve signed identity.
	signed, err := h.widmgr.Get(structs.WIHandle{
		IdentityName:       h.widName,
		WorkloadIdentifier: h.task.Name,
		WorkloadType:       structs.WorkloadTypeTask,
	})
	if err != nil {
		return "", 0, structs.NewRecoverableError(
			fmt.Errorf("failed to retrieve signed workload identity: %w", err),
			true,
		)
	}
	if signed == nil {
		return "", 0, structs.NewRecoverableError(
			errors.New("no signed workload identity available"),
			false,
		)
	}

	role := h.vaultConfig.Role
	if h.vaultBlock.Role != "" {
		role = h.vaultBlock.Role
	}

	// Derive Vault token with signed identity.
	token, renewable, leaseDuration, err := h.client.DeriveTokenWithJWT(ctx, vaultclient.JWTLoginRequest{
		JWT:       signed.JWT,

View on GitHub (pinned to 482b49bf1a)