{"record":{"id":"f2d2e908b5943a0b","repo":"chenhg5/cc-connect","slug":"session-key-is-required-f2d2e9","errorCode":null,"errorMessage":"session_key is required","messagePattern":"session_key is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/timer.go","lineNumber":62,"sourceCode":"// ExecutionTimeout returns how long the scheduler waits for the job goroutine to finish.\nfunc (j *TimerJob) ExecutionTimeout() time.Duration {\n\tif j.TimeoutMins == nil {\n\t\treturn defaultTimerJobTimeout\n\t}\n\tif *j.TimeoutMins <= 0 {\n\t\treturn 0\n\t}\n\treturn time.Duration(*j.TimeoutMins) * time.Minute\n}\n\n// UsesNewSessionPerRun reports whether the timer should use a new engine session.\nfunc (j *TimerJob) UsesNewSessionPerRun() bool {\n\treturn NormalizeCronSessionMode(j.SessionMode) == \"new_per_run\"\n}\n\nfunc validateTimerJob(j *TimerJob) error {\n\tif strings.TrimSpace(j.SessionKey) == \"\" {\n\t\treturn fmt.Errorf(\"session_key is required\")\n\t}\n\tif j.ScheduledAt.IsZero() {\n\t\treturn fmt.Errorf(\"scheduled_at is required\")\n\t}\n\tif j.Prompt == \"\" && j.Exec == \"\" {\n\t\treturn fmt.Errorf(\"either prompt or exec is required\")\n\t}\n\tif j.Prompt != \"\" && j.Exec != \"\" {\n\t\treturn fmt.Errorf(\"prompt and exec are mutually exclusive\")\n\t}\n\tmode := NormalizeCronSessionMode(j.SessionMode)\n\tif mode != \"\" && mode != \"new_per_run\" {\n\t\treturn fmt.Errorf(\"invalid session_mode %q (want reuse, new_per_run, or new-per-run)\", j.SessionMode)\n\t}\n\tif j.Mode != \"\" {\n\t\tswitch j.Mode {\n\t\tcase \"default\", \"bypassPermissions\", \"acceptEdits\", \"plan\", \"auto\", \"dontAsk\":\n\t\tdefault:","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/timer.go#L44-L80","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\njob := &core.TimerJob{ScheduledAt: when, Prompt: \"run tests\"}\nsched.AddJob(job) // error: session_key is required\n// after\njob := &core.TimerJob{SessionKey: sess.Key(), ScheduledAt: when, Prompt: \"run tests\"}\nsched.AddJob(job)","handlingStrategy":"validation","validationCode":"func validSessionKey(j *core.TimerJob) bool { return strings.TrimSpace(j.SessionKey) != \"\" }\nif !validSessionKey(job) { return errors.New(\"timer job needs a session_key\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["timer","validation","scheduler"],"backgroundTag":"missing-required-argument","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}