fix(errors): improve config error messages with section paths and remediation hints

Improve vague error messages in channel initialization and tool setup
to include specific config key paths and remediation steps, matching
the quality standard set by proxy validation errors.

Changes:
- telegram.rs: Include [channels.telegram] section path and required
  fields (bot_token, allowed_users) in missing-config error; add
  onboard hint; specify channels.telegram.allowed_users in pairing
  message; improve parse error context
- whatsapp.rs: Specify channels.whatsapp.allowed_numbers key path
  in unauthorized-number warning
- linq.rs: Specify channels.linq.allowed_senders key path in
  unauthorized-sender warning; add onboard hint
- web_search_tool.rs: Include tools.web_search.provider config path
  and valid values in unknown-provider error

Addresses API surface audit §8.2 (config context in error messages).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Alex Gorevski 2026-02-19 11:44:04 -08:00
parent 77609777ab
commit cc07cb66c3
4 changed files with 15 additions and 6 deletions

View file

@ -125,7 +125,8 @@ impl LinqChannel {
if !self.is_sender_allowed(&normalized_from) { if !self.is_sender_allowed(&normalized_from) {
tracing::warn!( tracing::warn!(
"Linq: ignoring message from unauthorized sender: {normalized_from}. \ "Linq: ignoring message from unauthorized sender: {normalized_from}. \
Add to allowed_senders in config.toml." Add to channels.linq.allowed_senders in config.toml, \
or run `zeroclaw onboard --channels-only` to configure interactively."
); );
return messages; return messages;
} }

View file

@ -387,7 +387,7 @@ impl TelegramChannel {
.await .await
.with_context(|| format!("Failed to read config file: {}", config_path.display()))?; .with_context(|| format!("Failed to read config file: {}", config_path.display()))?;
let mut config: Config = toml::from_str(&contents) let mut config: Config = toml::from_str(&contents)
.context("Failed to parse config file for Telegram binding")?; .context("Failed to parse config.toml — check [channels.telegram] section for syntax errors")?;
config.config_path = config_path; config.config_path = config_path;
config.workspace_dir = zeroclaw_dir.join("workspace"); config.workspace_dir = zeroclaw_dir.join("workspace");
Ok(config) Ok(config)
@ -396,7 +396,11 @@ impl TelegramChannel {
async fn persist_allowed_identity(&self, identity: &str) -> anyhow::Result<()> { async fn persist_allowed_identity(&self, identity: &str) -> anyhow::Result<()> {
let mut config = Self::load_config_without_env().await?; let mut config = Self::load_config_without_env().await?;
let Some(telegram) = config.channels_config.telegram.as_mut() else { let Some(telegram) = config.channels_config.telegram.as_mut() else {
anyhow::bail!("Telegram channel config is missing in config.toml"); anyhow::bail!(
"Missing [channels.telegram] section in config.toml. \
Add bot_token and allowed_users under [channels.telegram], \
or run `zeroclaw onboard --channels-only` to configure interactively"
);
}; };
let normalized = Self::normalize_identity(identity); let normalized = Self::normalize_identity(identity);
@ -691,7 +695,7 @@ impl TelegramChannel {
} else { } else {
let _ = self let _ = self
.send(&SendMessage::new( .send(&SendMessage::new(
" Telegram pairing is not active. Ask operator to update allowlist in config.toml.", " Telegram pairing is not active. Ask operator to add your user ID to channels.telegram.allowed_users in config.toml.",
&chat_id, &chat_id,
)) ))
.await; .await;

View file

@ -99,7 +99,8 @@ impl WhatsAppChannel {
if !self.is_number_allowed(&normalized_from) { if !self.is_number_allowed(&normalized_from) {
tracing::warn!( tracing::warn!(
"WhatsApp: ignoring message from unauthorized number: {normalized_from}. \ "WhatsApp: ignoring message from unauthorized number: {normalized_from}. \
Add to allowed_numbers in config.toml, then run `zeroclaw onboard --channels-only`." Add to channels.whatsapp.allowed_numbers in config.toml, \
or run `zeroclaw onboard --channels-only` to configure interactively."
); );
continue; continue;
} }

View file

@ -219,7 +219,10 @@ impl Tool for WebSearchTool {
let result = match self.provider.as_str() { let result = match self.provider.as_str() {
"duckduckgo" | "ddg" => self.search_duckduckgo(query).await?, "duckduckgo" | "ddg" => self.search_duckduckgo(query).await?,
"brave" => self.search_brave(query).await?, "brave" => self.search_brave(query).await?,
_ => anyhow::bail!("Unknown search provider: {}", self.provider), _ => anyhow::bail!(
"Unknown search provider: '{}'. Set tools.web_search.provider to 'duckduckgo' or 'brave' in config.toml",
self.provider
),
}; };
Ok(ToolResult { Ok(ToolResult {