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

  1. Set a non-empty SessionKey on the TimerJob before calling AddJob (use the active session's key from the engine).
  2. 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.
  3. Validate the job locally with validateTimerJob-equivalent checks before submitting to surface the problem at the call site.
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/f2d2e908b5943a0b. Report an issue: GitHub.