zeroclaw-labs/zeroclaw · error · anyhow::Error

Cannot persist empty Telegram identity

Error message

Cannot persist empty Telegram identity

What it means

persist_identity() normalizes the paired Telegram identity (Self::normalize_identity, which trims and keeps only usable fields such as the numeric id) before writing it into the telegram_<alias> config group. If normalization yields an empty value, the write aborts instead of persisting a blank identity entry.

Source

Thrown at crates/zeroclaw-channels/src/telegram.rs:1106

        self
    }

    async fn persist_allowed_identity(&self, identity: &str) -> anyhow::Result<()> {
        use zeroclaw_config::multi_agent::{PeerGroupConfig, PeerUsername};

        let Some(config) = &self.persist else {
            ::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!({"identity": identity})),
                "paired identity not persisted (no persistence handle wired)"
            );
            return Ok(());
        };
        let normalized = Self::normalize_identity(identity);
        if normalized.is_empty() {
            anyhow::bail!("Cannot persist empty Telegram identity");
        }
        let group_name = format!("telegram_{}", self.alias);
        let channel_ref: zeroclaw_config::providers::ChannelRef =
            format!("telegram.{}", self.alias).into();
        let snapshot = {
            let mut cfg = config.write();
            if !cfg.channels.telegram.contains_key(&self.alias) {
                anyhow::bail!(
                    "Missing [channels.telegram.{}] section. Run `zeroclaw config set channels.telegram.<alias>.bot_token <token>` to configure.",
                    self.alias
                );
            }
            let group = cfg
                .peer_groups
                .entry(group_name)
                .or_insert_with(|| PeerGroupConfig {
                    channel: channel_ref,
                    ..PeerGroupConfig::default()

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Log the raw identity before persisting and confirm the field normalize_identity keeps (the user id) is present and non-blank.
  2. Reject the pairing earlier — in the /start handler — when update.message.from is None, instead of building an empty identity.
  3. For anonymous admins/channel posts, derive identity from sender_chat or chat.id rather than a missing from.
  4. Trim-check inputs: whitespace-only ids normalize to empty and trigger this bail.

Example fix

// before
let identity = maybe_from.map(|f| Identity::from(f)).unwrap_or_default();
channel.persist_identity(identity).await?;

// after
let Some(from) = maybe_from else {
    anyhow::bail!("cannot pair: message has no sender (channel post or anonymous admin)");
};
channel.persist_identity(Identity::from(from)).await?;
Defensive patterns

Strategy: validation

Validate before calling

let normalized = TelegramChannel::normalize_identity(&identity);
if normalized.is_empty() {
    anyhow::bail!("cannot pair: identity has no usable id/username fields");
}
channel.persist_identity(identity).await?;

Type guard

fn has_persistable_identity(identity: &Identity) -> bool {
    identity.id.trim().is_empty() == false
        || identity.username.as_deref().map_or(false, |u| !u.trim().is_empty())
}

Try / catch

if let Err(e) = channel.persist_identity(identity).await {
    if e.to_string().contains("empty Telegram identity") {
        tracing::warn!("skipping persist for identity without sender fields");
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: A pairing update whose `message.from` is absent — channel posts, anonymous group admins, messages from linked channels — so the constructed identity has no id; a manually built or test Identity with empty/whitespace id and username reaching persist_identity.

Common situations: Pairing attempted from a channel post or anonymous-admin message instead of a private chat; test harnesses constructing Identity::default(); upstream parsing changes that stopped populating the id field.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/3e4f843514190a13. Report an issue: GitHub.