chenhg5/cc-connect · error

prompt and exec are mutually exclusive

Error message

prompt and exec are mutually exclusive

What it means

validateTimerJob enforces that Prompt and Exec are mutually exclusive: a timer job either sends a prompt to the agent or runs a shell command, not both. Setting both is ambiguous about intent, so AddJob refuses the job.

Source

Thrown at core/timer.go:71

}

// 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:
			return fmt.Errorf("invalid mode %q", j.Mode)
		}
	}
	if j.TimeoutMins != nil && *j.TimeoutMins < 0 {
		return fmt.Errorf("timeout_mins must be >= 0")
	}
	return nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Remove Exec if the job should send a prompt to the agent.
  2. Remove Prompt if the job should only run a shell command.
  3. Wrap the shell work as an instruction inside Prompt if both behaviors are truly needed.
  4. At config-load time, report which keys conflict so the user can delete one.

Example fix

// before
job := &core.TimerJob{SessionKey: k, ScheduledAt: when,
    Prompt: "deploy staging", Exec: "./deploy.sh"}
sched.AddJob(job) // mutually exclusive
// after
job := &core.TimerJob{SessionKey: k, ScheduledAt: when, Exec: "./deploy.sh"}
sched.AddJob(job)
Defensive patterns

Strategy: validation

Validate before calling

if job.Prompt != "" && job.Exec != "" { return errors.New("set only one of prompt or exec") }

Prevention

When it happens

Trigger: AddJob with a TimerJob where both Prompt != "" and Exec != "" — e.g. a config entry containing both `prompt` and `exec` keys, or builder code that assigns defaults to both fields.

Common situations: Users copying a timer template that included exec and then adding a prompt; programmatic construction that sets a default Exec and a caller-supplied Prompt; schema drift after adding the Exec feature to existing prompt-based configs.

Related errors


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