hashicorp/nomad · error

unexpected NotBeforeLeeway type: %v

Error message

unexpected NotBeforeLeeway type: %v

What it means

While decoding an ACL auth method's leeway settings from JSON, NotBeforeLeeway had an unexpected type (neither string nor number); the type switch in UnmarshalJSON rejects values it cannot convert to a duration.

Source

Thrown at nomad/structs/acl.go:1692

			}
		case float64:
			a.ExpirationLeeway = time.Duration(v)
		default:
			return fmt.Errorf("unexpected ExpirationLeeway type: %v", v)
		}
	}
	if aux.NotBeforeLeeway != nil {
		switch v := aux.NotBeforeLeeway.(type) {
		case string:
			if v != "" {
				if a.NotBeforeLeeway, err = time.ParseDuration(v); err != nil {
					return err
				}
			}
		case float64:
			a.NotBeforeLeeway = time.Duration(v)
		default:
			return fmt.Errorf("unexpected NotBeforeLeeway type: %v", v)
		}
	}
	if aux.ClockSkewLeeway != nil {
		switch v := aux.ClockSkewLeeway.(type) {
		case string:
			if v != "" {
				if a.ClockSkewLeeway, err = time.ParseDuration(v); err != nil {
					return err
				}
			}
		case float64:
			a.ClockSkewLeeway = time.Duration(v)
		default:
			return fmt.Errorf("unexpected ClockSkewLeeway type: %v", v)
		}
	}
	return nil
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Send NotBeforeLeeway as a duration string like "30s" or "2m".
  2. Or send a nanosecond number (e.g. 30000000000 for 30s).
  3. Validate the rendered JSON payload shape before POSTing it to the API.

Example fix

// before
{"NotBeforeLeeway": {"value": 30, "unit": "s"}}
// after
{"NotBeforeLeeway": "30s"}
Defensive patterns

Strategy: type-guard

Validate before calling

switch v := raw["NotBeforeLeeway"].(type) {
case string, float64:
	// ok
default:
	return fmt.Errorf("NotBeforeLeeway must be a duration string or number, got %T", v)
}

Type guard

func isDurationScalar(v interface{}) bool {
	switch v.(type) {
	case string, float64, float32, int64:
		return true
	}
	return false
}

Try / catch

if err := json.Unmarshal(payload, &am); err != nil {
	if strings.Contains(err.Error(), "unexpected NotBeforeLeeway type") {
		return fmt.Errorf("send NotBeforeLeeway as e.g. \"30s\" or a nanosecond number")
	}
	return err
}

Prevention

When it happens

Trigger: Submitting auth-method JSON where NotBeforeLeeway is a boolean, nested object, or array instead of a string duration or number.

Common situations: Copy-paste typos in the JWT/OIDC config block (putting the leeway under the wrong key as a map); template renderers emitting structured duration objects; editing config via UIs that serialize durations as objects.

Related errors


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