semaphoreui/semaphore · error

invalid --ttl value

Error message

invalid --ttl value: %w

What it means

createUserToken parses the --ttl flag with time.ParseDuration; if the value is not a valid Go duration string it returns 'invalid --ttl value: <cause>'. The TTL determines the token's expiry (now + duration); an unparseable value aborts token creation.

Solutions

  1. Use a valid Go duration: composite units like '24h', '720h' (30 days), '8760h' (a year), or '30m'
  2. Convert days to hours since Go has no 'd' unit (e.g. 7 days = 168h)
  3. Omit --ttl entirely for a non-expiring token

Example fix

// before
semaphore user token create --login john --ttl 30d
// after
semaphore user token create --login john --ttl 720h
Defensive patterns

Strategy: validation

Validate before calling

if ! [[ "$TTL" =~ ^[0-9]+(ns|us|µs|ms|s|m|h)+$ ]]; then echo "--ttl must be a Go duration like 720h"; exit 1; fi
semaphore user token create --login "$LOGIN" --ttl "$TTL"

Prevention

When it happens

Trigger: Running `semaphore user token create --login <u> --ttl <bad>` where <bad> is not a valid Go duration (e.g. '30d', '1w', '2 hours', or a bare number).

Common situations: Using day/week units that Go durations don't support ('30d'); including spaces ('2 hours'); forgetting the unit entirely ('30'); passing a relative date instead of a duration.

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


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/49957912f53c1650. Report an issue: GitHub.

Appendix: source

Thrown at cli/cmd/user_token.go:73

	}
	if err != nil {
		return db.User{}, err
	}

	return user, nil
}

func createUserToken(store db.Store, out io.Writer, args tokenArgs) error {
	user, err := getTokenUser(store, args.login)
	if err != nil {
		return err
	}

	var expiresAt *time.Time
	if args.ttl != "" {
		d, err := time.ParseDuration(args.ttl)
		if err != nil {
			return fmt.Errorf("invalid --ttl value: %w", err)
		}
		t := tz.Now().Add(d)
		expiresAt = &t
	}

	tokenID := make([]byte, 32)
	if _, err := io.ReadFull(rand.Reader, tokenID); err != nil {
		return err
	}

	token, err := store.CreateAPIToken(db.APIToken{
		ID:        strings.ToLower(base64.URLEncoding.EncodeToString(tokenID)),
		UserID:    user.ID,
		Expired:   false,
		ExpiresAt: expiresAt,
		Name:      args.name,
	})
	if err != nil {

View on GitHub (pinned to 1774ccb71a)