hashicorp/nomad · error

Identity %q is invalid: %w

Error message

Identity %q is invalid: %w

What it means

When a task declares a default identity block, Task.Validate calls WorkloadIdentity.Validate and wraps any failure as 'Identity %q is invalid: %w'. The inner error comes from the identity's own validation (missing name is impossible for the default, so usually audience/expiration/TTL problems).

Source

Thrown at nomad/structs/structs.go:8427

			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig must have a non-empty PluginID"))
		}

		if !CSIPluginTypeIsValid(t.CSIPluginConfig.Type) {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig PluginType must be one of 'node', 'controller', or 'monolith', got: \"%s\"", t.CSIPluginConfig.Type))
		}

		if t.CSIPluginConfig.StagePublishBaseDir != "" && t.CSIPluginConfig.MountDir != "" &&
			helper.IsSubdirectory(t.CSIPluginConfig.MountDir, t.CSIPluginConfig.StagePublishBaseDir) {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig StagePublishBaseDir must not be a subdirectory of MountDir, got: StagePublishBaseDir=\"%s\" MountDir=\"%s\"", t.CSIPluginConfig.StagePublishBaseDir, t.CSIPluginConfig.MountDir))
		}

		// TODO: Investigate validation of the PluginMountDir. Not much we can do apart from check IsAbs until after we understand its execution environment though :(
	}

	// Validate default Identity
	if t.Identity != nil {
		if err := t.Identity.Validate(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Identity %q is invalid: %w", t.Identity.Name, err))
		}
	}

	// Validate Identities
	for _, wid := range t.Identities {
		// Task.Canonicalize should move the default identity out of the Identities
		// slice, so if one is found that means it is a duplicate.
		if wid.Name == WorkloadIdentityDefaultName {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Duplicate default identities found"))
		}

		if err := wid.Validate(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Identity %q is invalid: %w", wid.Name, err))
		}
	}

	secrets := make(map[string]bool)
	for _, s := range t.Secrets {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped %w error after 'is invalid:' for the exact field problem
  2. Fix the identity block per error, e.g. use a valid Go duration for ttl ("1h", "30m")
  3. Run `nomad job validate job.hcl` locally to iterate on identity validation before submitting

Example fix

// before
identity {
  env = true
  ttl = "five minutes"
}
// after
identity {
  env = true
  ttl = "5m"
}
Defensive patterns

Strategy: validation

Validate before calling

if t.Identity != nil {
    if err := t.Identity.Validate(); err != nil {
        return fmt.Errorf("default identity bad: %w", err)
    }
}

Try / catch

if err := job.Validate(); err != nil {
    var mErr *structs.MultiError
    if errors.As(err, &mErr) { /* inspect wrapped identity errors */ }
}

Prevention

When it happens

Trigger: identity { } block in a task whose Validate fails, e.g. identity with an invalid ttl, negative expiration, or file/cloud-platform settings that don't parse; the wrapped error names the identity and the underlying reason.

Common situations: Misconfigured workload identity TTLs (ttl = "five minutes" instead of "5m"); env claiming identity blocks combined with file ones incorrectly; version drift where a new identity option isn't supported by the cluster.

Related errors


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