chenhg5/cc-connect · error

invalid session_mode %q (want reuse, new_per_run, or new-per

Error message

invalid session_mode %q (want reuse, new_per_run, or new-per-run)

What it means

validateCronJob normalizes SessionMode and rejects any value that isn't empty, "reuse", or a form of "new_per_run" (accepting reuse/new_per_run/new-per-run after normalization). This keeps the persisted session semantics limited to known modes so the fire-time scheduler never encounters an unrecognized mode.

Source

Thrown at core/cron.go:101

		return "new_per_run"
	default:
		return s
	}
}

func validateCronJob(j *CronJob) error {
	// SessionKey anchors the cron execution to a platform (ExecuteCronJob
	// derives platformName from the prefix before ":"). Without it the job
	// is persisted but fails at fire-time with the unhelpful
	// `platform "" not found for session ""`. Reject it up front so the
	// caller (management API, /cron/add, /cron edit) sees an immediate
	// 400 instead of a job that silently never runs.
	if strings.TrimSpace(j.SessionKey) == "" {
		return fmt.Errorf("session_key is required")
	}
	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 (want default, bypassPermissions, acceptEdits, plan, auto, or dontAsk)", j.Mode)
		}
	}
	if j.TimeoutMins != nil && *j.TimeoutMins < 0 {
		return fmt.Errorf("timeout_mins must be >= 0")
	}
	return nil
}

// CronStore persists cron jobs to a JSON file.
type CronStore struct {
	path string
	mu   sync.Mutex

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use one of: "reuse", "new_per_run", or "new-per-run" — or omit session_mode entirely for the default behavior.
  2. Check for typos/whitespace and use the exact lowercase snake-case form.
  3. Update the client/UI to a fixed enum of the three accepted values instead of free text.
  4. If you need a new mode, add it to NormalizeCronSessionMode and validateCronJob rather than sending an ad-hoc value.

Example fix

// before
job := core.CronJob{..., SessionMode: "newPerRun"}
core.AddJob(job) // 400
// after
job := core.CronJob{..., SessionMode: "new_per_run"}
core.AddJob(job)
Defensive patterns

Strategy: validation

Validate before calling

var validSessionModes = map[string]bool{"": true, "reuse": true, "new_per_run": true, "new-per-run": true}
func validateSessionMode(m string) error {
    if !validSessionModes[m] {
        return fmt.Errorf("invalid session_mode %q", m)
    }
    return nil
}

Try / catch

if err := validateSessionMode(job.SessionMode); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}
if err := core.AddJob(job); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
}

Prevention

When it happens

Trigger: Calling AddJob (or /cron add / edit) with SessionMode set to any string other than the accepted set — e.g. "new", "fresh", "New", "new-per-run " with stray characters, or a locale variant like "always_new".

Common situations: A UI dropdown sending free-text mode values; a typo like "reuse_session" or "newPerRun" (camelCase) that normalization doesn't recognize; clients written against an older spec that used different mode names; copy-paste from docs of another product.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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