{"record":{"id":"a5811dabf99e04d8","repo":"semaphoreui/semaphore","slug":"invalid-jwt-ttl-q-w","errorCode":null,"errorMessage":"invalid JWT TTL %q: %w","messagePattern":"invalid JWT TTL %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db/TemplateJWT.go","lineNumber":66,"sourceCode":"\t}\n}\n\n// Value implements driver.Valuer so TemplateJWTParams can be written to the database.\nfunc (p *TemplateJWTParams) Value() (driver.Value, error) {\n\tif p == nil {\n\t\treturn nil, nil\n\t}\n\treturn json.Marshal(p)\n}\n\n// ParsedTTL returns the parsed TTL or zero when unset.\nfunc (p *TemplateJWTParams) ParsedTTL() (time.Duration, error) {\n\tif p == nil || p.TTL == \"\" {\n\t\treturn 0, nil\n\t}\n\td, err := time.ParseDuration(p.TTL)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid JWT TTL %q: %w\", p.TTL, err)\n\t}\n\treturn d, nil\n}\n\n// Validate enforces some sanity checks on the JWT parameters to prevent misconfiguration and abuse.\nfunc (p *TemplateJWTParams) Validate() error {\n\tif p == nil || !p.Enabled {\n\t\treturn nil\n\t}\n\n\tif len(p.Audience) > maxJWTAudienceEntries {\n\t\treturn common_errors.NewValidationError(fmt.Sprintf(\"JWT audience must contain at most %d entries\", maxJWTAudienceEntries))\n\t}\n\tif slices.Contains(p.Audience, \"\") {\n\t\treturn common_errors.NewValidationError(\"JWT audience entries must not be empty\")\n\t}\n\n\tif p.TTL != \"\" {","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/db/TemplateJWT.go#L48-L84","documentation":"TemplateJWTParams.ParsedTTL converts the TTL string (e.g. '5m', '2h') into a time.Duration using time.ParseDuration. If the string is not a valid Go duration, it returns 'invalid JWT TTL %q' wrapped with the parse error. Validate calls this to reject misconfigured JWT parameters.","triggerScenarios":"Defining a template's jwt_params with a TTL like '300' (no unit), '5min', or '1h30' and then running template validation.","commonSituations":"Authors writing bare numbers expecting seconds; using human aliases like '5min' or '1d' that Go's ParseDuration doesn't accept ('min' works, 'd' does not); copying TTL formats from other systems (cron/ISO 8601).","solutions":["Use a valid Go duration string with a unit suffix, e.g. '5m', '30s', '2h'","Convert bare numbers to include a unit: '300' becomes '300s'","Add a template-validation step (calling Validate/ParsedTTL) in CI to catch bad TTLs before deployment"],"exampleFix":"// before\njwt_params:\n  ttl: \"300\"\n// after\njwt_params:\n  ttl: \"5m\"","handlingStrategy":"validation","validationCode":"if p.TTL != \"\" {\n    if _, err := time.ParseDuration(p.TTL); err != nil {\n        return fmt.Errorf(\"jwt ttl %q must be a Go duration like 30s, 5m, 2h\", p.TTL)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := params.Validate(); err != nil {\n    var derr error\n    _, derr = params.ParsedTTL()\n    if derr != nil && strings.Contains(derr.Error(), \"invalid JWT TTL\") {\n        return fmt.Errorf(\"fix jwt_params.ttl (use 30s/5m/2h format): %w\", derr)\n    }\n    return err\n}","preventionTips":["Always include a unit suffix (s, m, h) in TTL values","Never use bare numbers or units like 'd' that time.ParseDuration rejects","Run template Validate in CI to catch bad TTLs before deployment"],"tags":["jwt","duration","validation"],"backgroundTag":"invalid-duration-format","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}