{"record":{"id":"79ffdcf31cdd9fea","repo":"zeroclaw-labs/zeroclaw","slug":"cli-channel-factory-not-registered-call-register","errorCode":null,"errorMessage":"CLI channel factory not registered — call register_cli_channel_fn at startup","messagePattern":"CLI channel factory not registered — call register_cli_channel_fn at startup","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/agent/agent.rs","lineNumber":3251,"sourceCode":"                self.config.resolved.max_tool_iterations\n            )),\n            committed_response,\n            new_messages: new_msgs,\n        })\n    }\n\n    pub async fn run_single(&mut self, message: &str) -> Result<String> {\n        self.turn(message).await\n    }\n\n    pub async fn run_interactive(&mut self) -> Result<()> {\n        println!(\"🦀 ZeroClaw Interactive Mode\");\n        println!(\"Type /quit to exit.\\n\");\n\n        let (tx, mut rx) = tokio::sync::mpsc::channel(32);\n        let cli = crate::agent::loop_::CLI_CHANNEL_FN\n            .get()\n            .expect(\"CLI channel factory not registered — call register_cli_channel_fn at startup\")(\n        );\n\n        let listen_handle = zeroclaw_spawn::spawn!(async move {\n            let _ = zeroclaw_api::channel::Channel::listen(&*cli, tx).await;\n        });\n\n        while let Some(msg) = rx.recv().await {\n            let response = match self.turn(&msg.content).await {\n                Ok(resp) => resp,\n                Err(e) => {\n                    eprintln!(\"\\nError: {e}\\n\");\n                    continue;\n                }\n            };\n            println!(\"\\n{response}\\n\");\n        }\n\n        listen_handle.abort();","sourceCodeStart":3233,"sourceCodeEnd":3269,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/agent/agent.rs#L3233-L3269","documentation":"Interactive CLI mode obtains its channel from a process-global factory stored in the OnceLock CLI_CHANNEL_FN (crates/zeroclaw-runtime/src/agent/loop_.rs:17). The binary is expected to call register_cli_channel_fn exactly once at startup; entering interactive mode before that registration panics with this message.","triggerScenarios":"Invoking the interactive-mode entry point (agent.rs:3251 or loop_.rs:2175) inside a process that never called register_cli_channel_fn: a custom binary embedding the zeroclaw-runtime crate, an integration test driving interactive mode, or a startup-order change that defers registration.","commonSituations":"Embedding zeroclaw-runtime in your own executable and reusing its interactive loop; writing tests that exercise the agent loop without the full binary bootstrap; a refactor that moves the register_cli_channel_fn call after the interactive-mode branch.","solutions":["Call zeroclaw_runtime::agent::loop_::register_cli_channel_fn(Box::new(|| Box::new(your_cli_channel))) once during binary startup, before any interactive-mode code can run.","If you are writing a test or custom harness, register a stub channel factory in the test setup.","Compare with the main zeroclaw binary's startup sequence to confirm registration happens before the interactive branch."],"exampleFix":"// before (custom binary)\nfn main() {\n    zeroclaw_runtime::agent::interactive_main(); // panics: factory not registered\n}\n\n// after\nfn main() {\n    zeroclaw_runtime::agent::loop_::register_cli_channel_fn(Box::new(|| {\n        Box::new(zeroclaw_runtime::channel::cli::CliChannel::new())\n    }));\n    zeroclaw_runtime::agent::interactive_main();\n}","handlingStrategy":"validation","validationCode":"// Guard before entering interactive mode (custom binaries / tests):\nuse zeroclaw_runtime::agent::loop_::{CLI_CHANNEL_FN, register_cli_channel_fn};\nif CLI_CHANNEL_FN.get().is_none() {\n    register_cli_channel_fn(Box::new(|| Box::new(my_cli_channel())));\n}\n// Now safe to call the interactive entry point.","typeGuard":null,"tryCatchPattern":"std::panic::catch_unwind(|| agent::interactive_main())\n    .map_err(|_| anyhow::anyhow!(\"interactive mode unavailable: CLI channel factory not registered\"))?;","preventionTips":["Register the CLI channel factory as the first statement of main() in any binary embedding zeroclaw-runtime.","In tests, register a stub channel factory in the harness setup before driving the loop.","Keep registration before any branch that can reach interactive mode; do not defer it to lazy initialization."],"tags":["rust","startup","initialization-order","cli","panic","once-lock"],"backgroundTag":"startup-registration-missing","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}