{"record":{"id":"2a7646af6e12b68b","repo":"zeroclaw-labs/zeroclaw","slug":"missing-channels-line-section","errorCode":null,"errorMessage":"Missing [channels.line.{}] section","messagePattern":"Missing \\[channels\\.line\\.(.+?)\\] section","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/line.rs","lineNumber":258,"sourceCode":"        ::zeroclaw_log::record!(\n            WARN,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                .with_outcome(::zeroclaw_log::EventOutcome::Unknown)\n                .with_attrs(::serde_json::json!({\"user_id\": user_id})),\n            \"paired userId not persisted (no persistence handle wired)\"\n        );\n        return Ok(());\n    };\n    let normalized = user_id.trim().to_string();\n    if normalized.is_empty() {\n        anyhow::bail!(\"Cannot persist empty LINE userId\");\n    }\n    let group_name = format!(\"line_{}\", state.alias);\n    let channel_ref = ChannelRef::new(format!(\"line.{}\", state.alias));\n    let snapshot = {\n        let mut cfg = config.write();\n        if !cfg.channels.line.contains_key(&state.alias) {\n            anyhow::bail!(\"Missing [channels.line.{}] section\", state.alias);\n        }\n        let group = cfg\n            .peer_groups\n            .entry(group_name)\n            .or_insert_with(|| PeerGroupConfig {\n                channel: channel_ref,\n                ..PeerGroupConfig::default()\n            });\n        if group\n            .external_peers\n            .iter()\n            .any(|p| p.as_str() == normalized)\n        {\n            return Ok(());\n        }\n        group.external_peers.push(PeerUsername::new(normalized));\n        cfg.clone()\n    };","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/line.rs#L240-L276","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (config drifted: alias renamed while bot still serves old webhook)\n[channels.line.main2]\nchannel_secret = \"...\"\n\n# after\n[channels.line.main]\nchannel_secret = \"...\"","handlingStrategy":"validation","validationCode":"// At startup, prove every webhook alias has its config section\nfor alias in webhook_aliases() {\n    anyhow::ensure!(\n        config.read().channels.line.contains_key(alias),\n        \"webhook alias '{alias}' has no [channels.line.{alias}] section\"\n    );\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = persist_line_paired_identity(&user_id).await {\n    if e.to_string().contains(\"Missing [channels.line.\") {\n        alert(\"config drift: restart channel after alias/config changes\");\n    }\n    return Err(e);\n}","preventionTips":["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."],"tags":["line","config","alias","persistence","config-drift"],"backgroundTag":"missing-config-section","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}