zeroclaw-labs/zeroclaw · error
CLI channel factory not registered — call register_cli_chann
Error message
CLI channel factory not registered — call register_cli_channel_fn at startup
What it means
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.
Source
Thrown at crates/zeroclaw-runtime/src/agent/agent.rs:3251
self.config.resolved.max_tool_iterations
)),
committed_response,
new_messages: new_msgs,
})
}
pub async fn run_single(&mut self, message: &str) -> Result<String> {
self.turn(message).await
}
pub async fn run_interactive(&mut self) -> Result<()> {
println!("🦀 ZeroClaw Interactive Mode");
println!("Type /quit to exit.\n");
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
let cli = crate::agent::loop_::CLI_CHANNEL_FN
.get()
.expect("CLI channel factory not registered — call register_cli_channel_fn at startup")(
);
let listen_handle = zeroclaw_spawn::spawn!(async move {
let _ = zeroclaw_api::channel::Channel::listen(&*cli, tx).await;
});
while let Some(msg) = rx.recv().await {
let response = match self.turn(&msg.content).await {
Ok(resp) => resp,
Err(e) => {
eprintln!("\nError: {e}\n");
continue;
}
};
println!("\n{response}\n");
}
listen_handle.abort();View on GitHub (pinned to 88bb9c8533)
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.
Example fix
// before (custom binary)
fn main() {
zeroclaw_runtime::agent::interactive_main(); // panics: factory not registered
}
// after
fn main() {
zeroclaw_runtime::agent::loop_::register_cli_channel_fn(Box::new(|| {
Box::new(zeroclaw_runtime::channel::cli::CliChannel::new())
}));
zeroclaw_runtime::agent::interactive_main();
} Defensive patterns
Strategy: validation
Validate before calling
// Guard before entering interactive mode (custom binaries / tests):
use zeroclaw_runtime::agent::loop_::{CLI_CHANNEL_FN, register_cli_channel_fn};
if CLI_CHANNEL_FN.get().is_none() {
register_cli_channel_fn(Box::new(|| Box::new(my_cli_channel())));
}
// Now safe to call the interactive entry point. Try / catch
std::panic::catch_unwind(|| agent::interactive_main())
.map_err(|_| anyhow::anyhow!("interactive mode unavailable: CLI channel factory not registered"))?; Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to build reqwest client
- purge_agent not supported by this memory backend
- rename_agent not supported by this memory backend
- memory backend '{}' does not support StoreOptions kind/pinne
- memory backend '{}' does not support agent-attributed StoreO
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/79ffdcf31cdd9fea.
Report an issue: GitHub.