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

Slack outbound attachment target must be a local workspace p

Error message

Slack outbound attachment target must be a local workspace path

What it means

Raised by SlackChannel::resolve_outbound_attachment_marker when the target of a parsed outbound attachment marker ([image:...] or [file:...] in outgoing message text) starts with 'file:', 'data:', or contains '://'. This is a deliberate security guard: Slack outbound attachments must reference local workspace files, never URLs or inline data, because the channel uploads the file bytes itself after canonicalizing them inside workspace_dir.

Source

Thrown at crates/zeroclaw-channels/src/slack.rs:1031

        Ok(())
    }

    fn http_client(&self) -> reqwest::Client {
        zeroclaw_config::schema::build_channel_proxy_client_with_timeouts(
            "channel.slack",
            self.proxy_url.as_deref(),
            30,
            10,
        )
    }

    async fn resolve_outbound_attachment_marker(
        &self,
        marker: &SlackOutboundAttachmentMarker,
    ) -> anyhow::Result<MediaAttachment> {
        let target = marker.target.trim();
        if target.starts_with("file:") || target.starts_with("data:") || target.contains("://") {
            anyhow::bail!("Slack outbound attachment target must be a local workspace path");
        }

        let path = Path::new(target);
        if !path.is_absolute() {
            anyhow::bail!("Slack outbound attachment path must be absolute: {target}");
        }

        let workspace = self
            .workspace_dir
            .as_deref()
            .context("Slack outbound local attachments require workspace_dir")?;
        let canonical_workspace = tokio::fs::canonicalize(workspace).await.with_context(|| {
            format!(
                "failed to canonicalize Slack workspace {}",
                workspace.display()
            )
        })?;
        let canonical_path = tokio::fs::canonicalize(path)

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Change the marker target to an absolute local workspace path and let Slack upload the bytes
  2. If the content only exists remotely, download it into the workspace first, then reference the local path
  3. Strip or rewrite malformed markers before sending if no upload is intended

Example fix

// before — marker points at a remote URL
let msg = "report ready [image:https://cdn.example.com/chart.png]";

// after — download once, reference the local workspace file
let path = workspace_dir.join("attachments/chart.png"); // populated beforehand
let msg = format!("report ready [image:{}]", path.display());
Defensive patterns

Strategy: validation

Validate before calling

fn sanitize_attachment_markers(msg: &str, workspace_dir: &Path) -> String {
    // rewrite [image:<url>] / [file:<url>] markers into local workspace paths,
    // dropping markers whose content cannot be downloaded
    rewrite_markers(msg, |kind, target| {
        if is_local_workspace_target(target) {
            Some(format!("[{kind}:{target}]"))
        } else {
            download_to_workspace(target, workspace_dir)
                .ok()
                .map(|p| format!("[{kind}:{}]", p.display()))
        }
    })
}

Type guard

fn is_local_workspace_target(target: &str) -> bool {
    !(target.starts_with("file:") || target.starts_with("data:") || target.contains("://"))
}

Prevention

When it happens

Trigger: Outgoing message text contains a marker like [image:https://cdn.example.com/pic.png] or [file:data:text/plain;base64,...]; parse_outbound_attachment_markers extracts it and resolve_outbound_attachment_marker rejects the target scheme before any path work.

Common situations: An agent or template emits remote URLs or data URIs in attachment markers, expecting them to be forwarded as-is; porting behavior from another channel that accepted URLs; markdown link syntax colliding with the marker grammar.

Related errors


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