zeroclaw-labs/zeroclaw · error · anyhow::Error
Missing [channels.line.{}] section
Error message
Missing [channels.line.{}] section What it means
persist_line_paired_identity takes the live-config write lock and requires the [channels.line.<alias>] section for the webhook's alias to exist; if it is missing it bails before mutating anything. This fires when the alias embedded in the bot's runtime state (from the webhook URL) no longer matches the config file — config drift between wiring time and persistence time. The missing section name is included in the message.
Source
Thrown at crates/zeroclaw-channels/src/line.rs:258
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
.with_outcome(::zeroclaw_log::EventOutcome::Unknown)
.with_attrs(::serde_json::json!({"user_id": user_id})),
"paired userId not persisted (no persistence handle wired)"
);
return Ok(());
};
let normalized = user_id.trim().to_string();
if normalized.is_empty() {
anyhow::bail!("Cannot persist empty LINE userId");
}
let group_name = format!("line_{}", state.alias);
let channel_ref = ChannelRef::new(format!("line.{}", state.alias));
let snapshot = {
let mut cfg = config.write();
if !cfg.channels.line.contains_key(&state.alias) {
anyhow::bail!("Missing [channels.line.{}] section", state.alias);
}
let group = cfg
.peer_groups
.entry(group_name)
.or_insert_with(|| PeerGroupConfig {
channel: channel_ref,
..PeerGroupConfig::default()
});
if group
.external_peers
.iter()
.any(|p| p.as_str() == normalized)
{
return Ok(());
}
group.external_peers.push(PeerUsername::new(normalized));
cfg.clone()
};View on GitHub (pinned to 88bb9c8533)
Solutions
- Restore (or rename back) the [channels.line.<alias>] section so it matches the alias in the running webhook URL.
- Restart the channel process after alias or section changes so runtime state is re-wired from current config.
- Keep aliases stable across environments to avoid re-pairing surprises.
Example fix
# before (config drifted: alias renamed while bot still serves old webhook) [channels.line.main2] channel_secret = "..." # after [channels.line.main] channel_secret = "..."
Defensive patterns
Strategy: validation
Validate before calling
// At startup, prove every webhook alias has its config section
for alias in webhook_aliases() {
anyhow::ensure!(
config.read().channels.line.contains_key(alias),
"webhook alias '{alias}' has no [channels.line.{alias}] section"
);
} Try / catch
if let Err(e) = persist_line_paired_identity(&user_id).await {
if e.to_string().contains("Missing [channels.line.") {
alert("config drift: restart channel after alias/config changes");
}
return Err(e);
} Prevention
- Validate alias-to-section consistency at channel startup and after config reloads.
- Restart channels whenever aliases or channel sections change.
- Prefer stable aliases across environments.
When it happens
Trigger: The alias was renamed or its section removed from config while the channel was running on the old alias; a hot-reload replaced config mid-session; the channel was wired from one config and the webhook arrived against another.
Common situations: Renaming a LINE alias during environment migration, partial config deploys that drop the section, or stale runtime state after editing zeroclaw.toml without restarting the channel.
Related errors
- cli-skills-agent-not-configured
- unsupported room visibility '{other}': expected private or p
- channel '{}' does not support forge API requests
- Destination {} is not in allowed list
- gateway registration failed ({status}): {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/2a7646af6e12b68b.
Report an issue: GitHub.