chenhg5/cc-connect · error
delay must be positive
Error message
delay must be positive
What it means
When ParseDelayOrTime's input parses successfully as a Go duration (time.ParseDuration succeeds) but the duration is zero or negative, the function rejects it with 'delay must be positive'. A timer scheduled in the past or exactly now would never usefully fire, so non-positive delays are invalid.
Source
Thrown at core/timer.go:457
if _, err := rand.Read(b); err != nil {
panic(fmt.Errorf("generate timer id: %w", err))
}
return hex.EncodeToString(b)
}
// ParseDelayOrTime parses a relative duration ("2h", "30m", "1h30m") or
// an absolute ISO time ("2026-05-15T14:00", "2026-05-15T14:00:00+08:00")
// and returns the absolute fire time.
func ParseDelayOrTime(s string) (time.Time, error) {
s = strings.TrimSpace(s)
if s == "" {
return time.Time{}, fmt.Errorf("empty delay or time")
}
// Try as a Go duration first (e.g., "2h", "30m", "1h30m", "2h30m15s")
if d, err := time.ParseDuration(s); err == nil {
if d <= 0 {
return time.Time{}, fmt.Errorf("delay must be positive")
}
return time.Now().Add(d), nil
}
// Try ISO time formats
// RFC3339 includes timezone (e.g. "2026-05-15T14:00:00+08:00"),
// so it's parsed directly. The other layouts have no timezone
// and are interpreted in the system's local timezone.
layouts := []struct {
layout string
local bool
}{
{time.RFC3339, false},
{"2006-01-02T15:04:05", true},
{"2006-01-02T15:04", true},
{"2006-01-02 15:04:05", true},
{"2006-01-02 15:04", true},
}View on GitHub (pinned to 4000b2338a)
Solutions
- Pass a strictly positive duration, e.g. '1m' or '2h30m'.
- Validate the computed duration before calling: if d <= 0, reject or bump to a minimum delay.
- For absolute scheduling, use an ISO time string instead of a duration.
Example fix
// before
if err := run("/timer add " + remaining); err != nil { ... } // remaining may be <= 0
// after
if remaining <= time.Minute {
remaining = time.Minute
}
if err := run("/timer add " + remaining.String()); err != nil { ... } Defensive patterns
Strategy: validation
Validate before calling
if d, err := time.ParseDuration(arg); err == nil && d <= 0 {
return fmt.Errorf("timer delay must be positive, got %s", arg)
} Try / catch
at, err := core.ParseDelayOrTime(arg)
if err != nil {
if strings.Contains(err.Error(), "must be positive") {
reply("Delay must be greater than zero, e.g. 30m")
return
}
return err
} Prevention
- Clamp computed delays to a small minimum (e.g. 1 minute).
- Reject negative/zero values at the UI layer with a clear hint.
- When deriving delays from timestamps, check the target is in the future first.
When it happens
Trigger: Calling ParseDelayOrTime("0"), ParseDelayOrTime("-5m"), ParseDelayOrTime("0s") — any string accepted by time.ParseDuration that evaluates to d <= 0.
Common situations: User typo like '-30m' instead of '30m'; computing a remaining delay from a stale timestamp that has already elapsed yielding zero or negative; a scripted command passing '0' as a placeholder.
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
- session_key is required
- scheduled_at is required
- either prompt or exec is required
- prompt and exec are mutually exclusive
- invalid session_mode %q (want reuse, new_per_run, or new-per
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/b6abec766ec09b6e.
Report an issue: GitHub.