{"record":{"id":"bf0d30e117ef57ef","repo":"hashicorp/nomad","slug":"ttl-must-be-0","errorCode":null,"errorMessage":"ttl must be >= 0","messagePattern":"ttl must be >= 0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/workload_id.go","lineNumber":490,"sourceCode":"\t\tif wi.ChangeSignal != \"\" {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"can only use change_signal=%q with change_mode=%q\",\n\t\t\t\twi.ChangeSignal, WIChangeModeSignal))\n\t\t}\n\tcase WIChangeModeSignal:\n\t\tif wi.ChangeSignal == \"\" {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"change_signal must be specified when using change_mode=%q\", WIChangeModeSignal))\n\t\t}\n\tdefault:\n\t\t// Unknown change_mode\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"invalid change_mode: %s\", wi.ChangeMode))\n\t}\n\n\tif wi.TTL > 0 && (wi.Name == \"\" || wi.Name == WorkloadIdentityDefaultName) {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"ttl for default identity not yet supported\"))\n\t}\n\n\tif wi.TTL < 0 {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"ttl must be >= 0\"))\n\t}\n\n\tif wi.Filepath != \"\" && !wi.File {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"file parameter must be true in order to specify filepath\"))\n\t}\n\n\treturn mErr.ErrorOrNil()\n}\n\nfunc (wi *WorkloadIdentity) Warnings() error {\n\tif wi == nil {\n\t\treturn fmt.Errorf(\"must not be nil\")\n\t}\n\n\tvar mErr multierror.Error\n\n\tif n := len(wi.Audience); n == 0 {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"identities without an audience are insecure\"))","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/workload_id.go#L472-L508","documentation":"WorkloadIdentity.Validate requires TTL >= 0. A negative TTL is nonsensical (identity files/tokens cannot expire in the past by negative duration), so validation flags it as a configuration error, typically the result of a bad duration parse or arithmetic.","triggerScenarios":"Setting structs.WorkloadIdentity.TTL to a negative time.Duration, e.g. from a misparsed string like \"-1h\" or subtracting timestamps before assignment, then calling Validate().","commonSituations":"Programmatic job generation computing TTL as (target - now) when target is in the past; environment variable or template interpolation yielding a negative duration; unit mistakes like \"-5m\".","solutions":["Correct the TTL value to a non-negative duration (e.g. \"1h\").","Clamp computed durations with a check: if ttl < 0 { ttl = 0 } before assigning.","Fix the source value (env var, template, config) that produced the negative duration."],"exampleFix":"// before\nwi.TTL = target.Sub(time.Now()) // negative if target passed\n// after\nttl := target.Sub(time.Now())\nif ttl < 0 {\n  ttl = 0\n}\nwi.TTL = ttl","handlingStrategy":"validation","validationCode":"func validateTTLNonNegative(wi *structs.WorkloadIdentity) error {\n  if wi.TTL < 0 {\n    return fmt.Errorf(\"ttl must be >= 0, got %s\", wi.TTL)\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Parse TTLs with time.ParseDuration and reject negative results at config load.","Clamp computed durations to zero before assignment.","Never compute TTL by subtracting timestamps that may be in the past."],"tags":["nomad","workload-identity","validation","ttl"],"backgroundTag":"invalid-argument-value","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}