hashicorp/nomad · error

unable to find token for workload %q and identity %q

Error message

unable to find token for workload %q and identity %q

What it means

WIDMgr.Get is the synchronous accessor tasks use to obtain a signed identity token; it returns an error if no token is cached for the given WIHandle. By design every identity should have a token by the time Get is called, so this signals a lifecycle bug or premature access.

Source

Thrown at client/widmgr/widmgr.go:150

	}

	go m.renew()

	return nil
}

// Get retrieves the latest signed identity or returns an error. It must be
// called after Run and does not block.
//
// For retrieving tokens which might be renewed callers should use Watch
// instead to avoid missing new tokens retrieved by Run between Get and Watch
// calls.
func (m *WIDMgr) Get(id structs.WIHandle) (*structs.SignedWorkloadIdentity, error) {
	token := m.get(id)
	if token == nil {
		// This is an error as every identity should have a token by the time Get
		// is called.
		return nil, fmt.Errorf("unable to find token for workload %q and identity %q", id.WorkloadIdentifier, id.IdentityName)
	}

	return token, nil
}

func (m *WIDMgr) get(id structs.WIHandle) *structs.SignedWorkloadIdentity {
	m.lastTokenLock.RLock()
	defer m.lastTokenLock.RUnlock()

	return m.lastToken[id]
}

// Watch returns a channel that sends new signed identities until it is closed
// due to shutdown. Must be called after Run.
//
// The caller must call the returned func to stop watching and ensure the
// watched id actually exists, otherwise the channel never returns a result.
func (m *WIDMgr) Watch(id structs.WIHandle) (<-chan *structs.SignedWorkloadIdentity, func()) {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure Run() completed and signing succeeded before calling Get.
  2. Verify the WIHandle matches exactly the workload identifier and identity name that were requested.
  3. Check logs for earlier 'failed to fetch signed identities' errors indicating the manager never got tokens.
  4. Restart the task/agent so identities are re-requested and cached.

Example fix

// before
tok, err := widMgr.Get(structs.WIHandle{WorkloadIdentifier: tgName, IdentityName: "vault_token"})
// after
handle := alloc.TaskServicesWIHandleForIdentity(tgName, "vault_token") // derive from alloc, not hand-built
tok, err := widMgr.Get(handle)
if err != nil {
    return fmt.Errorf("identity not yet available: %w", err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the manager is ready before Get
if !widMgr.Ready() { // or wait on a ready channel
    return fmt.Errorf("widmgr not initialized")
}

Type guard

func tokenAvailable(m *WIDMgr, id structs.WIHandle) bool { return m.get(id) != nil }

Try / catch

tok, err := widMgr.Get(handle)
if err != nil {
    if strings.Contains(err.Error(), "unable to find token") {
        // wait for renewal/signing, then retry once
        time.Sleep(backoff)
        tok, err = widMgr.Get(handle)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling Get with a WIHandle whose WorkloadIdentifier/IdentityName was never requested (or whose token was fetched but not yet stored), or after signing failed and Run's refresh never populated the manager.

Common situations: Task hook calling Get before Run/getInitialIdentities completed; identity name typo or handle built from wrong workload; signing previously failed so nothing was cached; restart where state DB lost the token.

Related errors


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