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

iMessage send failed: {stderr}

Error message

iMessage send failed: {stderr}

What it means

IMessageChannel::send runs an osascript AppleScript that tells Messages.app to send the text; when the process exits non-zero, the captured stderr becomes the error. The AppleScript itself escaped both message and target against CWE-78 injection, so a non-zero exit is environmental: Messages.app state, AppleScript/Automation permissions, or an unreachable recipient — the stderr text carries the real reason.

Source

Thrown at crates/zeroclaw-channels/src/imessage.rs:180

        let escaped_target = escape_applescript(&message.recipient);

        let script = format!(
            r#"tell application "Messages"
    set targetService to 1st account whose service type = iMessage
    set targetBuddy to participant "{escaped_target}" of targetService
    send "{escaped_msg}" to targetBuddy
end tell"#
        );

        let output = tokio::process::Command::new("osascript")
            .arg("-e")
            .arg(&script)
            .output()
            .await?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            anyhow::bail!("iMessage send failed: {stderr}");
        }

        Ok(())
    }

    async fn listen(&self, tx: mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {
        ::zeroclaw_log::record!(
            INFO,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),
            "iMessage channel listening (AppleScript bridge)..."
        );

        // Query the Messages SQLite database for new messages
        // The database is at ~/Library/Messages/chat.db
        let db_path = UserDirs::new()
            .map(|u| u.home_dir().join("Library/Messages/chat.db"))
            .ok_or_else(|| {
                ::zeroclaw_log::record!(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Reproduce manually to see the real AppleScript error: run the same 'tell application "Messages" ... send ...' snippet via osascript -e in the same terminal/session
  2. Grant Automation permission: System Settings > Privacy & Security > Automation > [your terminal/zeroclaw] > Messages
  3. Open Messages.app, sign into iCloud, confirm iMessage is active, and verify the recipient works from the GUI
  4. If the target is SMS-only, route through a different channel — the AppleScript bridge only does iMessage
Defensive patterns

Strategy: try-catch

Try / catch

match channel.send(&msg).await {
    Err(e) if e.to_string().contains("iMessage send failed") => {
        // e carries osascript stderr verbatim: match on 'Not authorized' (Automation perm),
        // 'not signed in' (iCloud), or registration errors; route message to a fallback channel
    }
    other => other?,
}

Prevention

When it happens

Trigger: osascript exits non-zero: Messages.app not signed into an iCloud account with iMessage enabled; macOS Automation permission not granted to the process invoking osascript ('Not authorized to send Apple events to Messages'); recipient not registered with iMessage; Messages.app not running/first-run dialogs pending.

Common situations: Fresh mac that never opened Messages.app; zeroclaw launched from a terminal or launchd agent missing in System Settings > Privacy & Security > Automation; recipient is an SMS-only number; macOS update reset TCC permissions.

Related errors


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