{"record":{"id":"d6ed88ba10665785","repo":"sipeed/picoclaw","slug":"process-options-not-available","errorCode":null,"errorMessage":"process options not available","messagePattern":"process options not available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/agent_command.go","lineNumber":279,"sourceCode":"\t\t\tinfo := al.GetActiveTurn()\n\t\t\tif info == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn info\n\t\t},\n\t\tSwitchChannel: func(value string) error {\n\t\t\tif al.channelManager == nil {\n\t\t\t\treturn fmt.Errorf(\"channel manager not initialized\")\n\t\t\t}\n\t\t\tif _, exists := al.channelManager.GetChannel(value); !exists && value != \"cli\" {\n\t\t\t\treturn fmt.Errorf(\"channel '%s' not found or not enabled\", value)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\trt.StopActiveTurn = func() (commands.StopResult, error) {\n\t\tif opts == nil {\n\t\t\treturn commands.StopResult{}, fmt.Errorf(\"process options not available\")\n\t\t}\n\t\treturn al.stopActiveTurnForSession(opts.Dispatch.SessionKey)\n\t}\n\tif agent != nil && agent.ContextBuilder != nil {\n\t\trt.ListSkillNames = agent.ContextBuilder.ListSkillNames\n\t}\n\trt.ReloadConfig = func() error {\n\t\tif al.reloadFunc == nil {\n\t\t\treturn fmt.Errorf(\"reload not configured\")\n\t\t}\n\t\treturn al.reloadFunc()\n\t}\n\tif agent != nil {\n\t\tif agent.ContextBuilder != nil {\n\t\t\trt.ListSkillNames = agent.ContextBuilder.ListSkillNames\n\t\t}\n\t\trt.GetModelInfo = func() (string, string) {\n\t\t\treturn agent.Model, resolvedCandidateProvider(agent.Candidates, cfg.Agents.Defaults.Provider)","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/agent_command.go#L261-L297","documentation":"The StopActiveTurn hook requires the process options (opts) to know the dispatch session key it must interrupt; opts == nil means the runtime was built without process options, so there is no session to stop. The hook returns before calling stopActiveTurnForSession.","triggerScenarios":"Issuing a stop/interrupt command in an AgentLoop constructed without ProcessOptions (tests, embedded runtimes); calling rt.StopActiveTurn programmatically on a bare runtime; startup ordering where opts are injected after the command table.","commonSituations":"Embedders wiring commands before process options exist; unit tests exercising the runtime in isolation; refactors that made opts optional in one constructor path.","solutions":["Pass process options (with Dispatch.SessionKey set) when constructing the agent loop/runtime","Guard callers: only surface the stop command when process options are available","If using the stock CLI, restart the process — this indicates broken startup wiring"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"if opts == nil { /* do not expose stop/turn commands this session */ }","typeGuard":"func stopAvailable(opts *ProcessOptions) bool { return opts != nil }","tryCatchPattern":"_, err := rt.StopActiveTurn()\nif err != nil && strings.Contains(err.Error(), \"process options not available\") {\n    // wiring defect: surface loudly, never retry-loop\n}","preventionTips":["Always construct runtimes with process options that carry Dispatch.SessionKey","Register turn/session commands only when opts are present","Assert opts != nil in constructors when the command set is enabled"],"tags":["runtime","initialization","session"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}