semaphoreui/semaphore · error
invalid JWT TTL
Error message
invalid JWT TTL %q: %w
What it means
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.
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
Example fix
// before jwt_params: ttl: "300" // after jwt_params: ttl: "5m"
Defensive patterns
Strategy: validation
Validate before calling
if p.TTL != "" {
if _, err := time.ParseDuration(p.TTL); err != nil {
return fmt.Errorf("jwt ttl %q must be a Go duration like 30s, 5m, 2h", p.TTL)
}
} Try / catch
if err := params.Validate(); err != nil {
var derr error
_, derr = params.ParsedTTL()
if derr != nil && strings.Contains(derr.Error(), "invalid JWT TTL") {
return fmt.Errorf("fix jwt_params.ttl (use 30s/5m/2h format): %w", derr)
}
return err
} Prevention
- 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
When it happens
Trigger: Defining a template's jwt_params with a TTL like '300' (no unit), '5min', or '1h30' and then running template validation.
Common situations: 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).
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
Related errors
- internal error
- secret does not belong to this environment
- name can not be empty
- missing secret
- invalid environment secret type
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/a5811dabf99e04d8.
Report an issue: GitHub.
Appendix: source
Thrown at db/TemplateJWT.go:66
}
}
// Value implements driver.Valuer so TemplateJWTParams can be written to the database.
func (p *TemplateJWTParams) Value() (driver.Value, error) {
if p == nil {
return nil, nil
}
return json.Marshal(p)
}
// ParsedTTL returns the parsed TTL or zero when unset.
func (p *TemplateJWTParams) ParsedTTL() (time.Duration, error) {
if p == nil || p.TTL == "" {
return 0, nil
}
d, err := time.ParseDuration(p.TTL)
if err != nil {
return 0, fmt.Errorf("invalid JWT TTL %q: %w", p.TTL, err)
}
return d, nil
}
// Validate enforces some sanity checks on the JWT parameters to prevent misconfiguration and abuse.
func (p *TemplateJWTParams) Validate() error {
if p == nil || !p.Enabled {
return nil
}
if len(p.Audience) > maxJWTAudienceEntries {
return common_errors.NewValidationError(fmt.Sprintf("JWT audience must contain at most %d entries", maxJWTAudienceEntries))
}
if slices.Contains(p.Audience, "") {
return common_errors.NewValidationError("JWT audience entries must not be empty")
}
if p.TTL != "" {View on GitHub (pinned to 1774ccb71a)