chenhg5/cc-connect · error
session_key is required
Error message
session_key is required
What it means
validateTimerJob in core/timer.go rejects any TimerJob whose SessionKey is empty or whitespace-only when it is submitted via TimerScheduler.AddJob. The session key identifies which agent session the scheduled prompt should run in, so a job without one is unusable and is refused at registration time rather than failing silently at fire time.
Source
Thrown at core/timer.go:62
// ExecutionTimeout returns how long the scheduler waits for the job goroutine to finish.
func (j *TimerJob) ExecutionTimeout() time.Duration {
if j.TimeoutMins == nil {
return defaultTimerJobTimeout
}
if *j.TimeoutMins <= 0 {
return 0
}
return time.Duration(*j.TimeoutMins) * time.Minute
}
// UsesNewSessionPerRun reports whether the timer should use a new engine session.
func (j *TimerJob) UsesNewSessionPerRun() bool {
return NormalizeCronSessionMode(j.SessionMode) == "new_per_run"
}
func validateTimerJob(j *TimerJob) error {
if strings.TrimSpace(j.SessionKey) == "" {
return fmt.Errorf("session_key is required")
}
if j.ScheduledAt.IsZero() {
return fmt.Errorf("scheduled_at is required")
}
if j.Prompt == "" && j.Exec == "" {
return fmt.Errorf("either prompt or exec is required")
}
if j.Prompt != "" && j.Exec != "" {
return fmt.Errorf("prompt and exec are mutually exclusive")
}
mode := NormalizeCronSessionMode(j.SessionMode)
if mode != "" && mode != "new_per_run" {
return fmt.Errorf("invalid session_mode %q (want reuse, new_per_run, or new-per-run)", j.SessionMode)
}
if j.Mode != "" {
switch j.Mode {
case "default", "bypassPermissions", "acceptEdits", "plan", "auto", "dontAsk":
default:View on GitHub (pinned to 4000b2338a)
Solutions
- Set a non-empty SessionKey on the TimerJob before calling AddJob (use the active session's key from the engine).
- If the job should always start a fresh session, set SessionKey to a stable logical key and SessionMode to "new_per_run" so the key is only an identity, not a reuse target.
- Validate the job locally with validateTimerJob-equivalent checks before submitting to surface the problem at the call site.
- Check config parsing: ensure the session_key key in config.toml/JSON maps to the SessionKey field.
Example fix
// before
job := &core.TimerJob{ScheduledAt: when, Prompt: "run tests"}
sched.AddJob(job) // error: session_key is required
// after
job := &core.TimerJob{SessionKey: sess.Key(), ScheduledAt: when, Prompt: "run tests"}
sched.AddJob(job) Defensive patterns
Strategy: validation
Validate before calling
func validSessionKey(j *core.TimerJob) bool { return strings.TrimSpace(j.SessionKey) != "" }
if !validSessionKey(job) { return errors.New("timer job needs a session_key") } Prevention
- Always derive SessionKey from the live session object, never hand-type it
- Run validateTimerJob-style checks at config-load time so bad entries fail fast
- Include session_key in config example templates and docs
When it happens
Trigger: Calling AddJob with a &TimerJob{...} where SessionKey is "" or only spaces (e.g. constructing the job from parsed TOML/JSON where the session_key field was omitted, or copying a struct without setting SessionKey).
Common situations: Hand-written config entries missing session_key; programmatic job creation where the caller has the prompt and schedule but forgot to resolve the current session's key; refactors that renamed the session-key field and left the assignment behind.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- scheduled_at is required
- either prompt or exec is required
- session_key is required
- invalid session_mode %q (want reuse, new_per_run, or new-per
- prompt and exec are mutually exclusive
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/f2d2e908b5943a0b.
Report an issue: GitHub.