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

Slack outbound attachment path must be absolute: {target}

Error message

Slack outbound attachment path must be absolute: {target}

What it means

Raised by SlackChannel::resolve_outbound_attachment_marker when the marker's target passes the URL guard but Path::new(target).is_absolute() is false — e.g. [image:attachments/chart.png]. Slack outbound attachment resolution joins the path against the canonicalized workspace_dir, so it needs an absolute path to canonicalize; relative targets are rejected rather than guessed.

Source

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

            "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)
            .await
            .with_context(|| format!("Slack outbound attachment path not found: {target}"))?;

        if !canonical_path.starts_with(&canonical_workspace) {
            anyhow::bail!(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Emit the absolute path: join the relative path with workspace_dir when building the marker string
  2. Ensure the file exists at the absolute location before sending (a nonexistent path produces the adjacent 'not found' context error)
  3. Keep markers stable once sent — the same absolute path must remain valid for re-sends

Example fix

// before
let msg = format!("[image:{}]", rel_path.display()); // e.g. attachments/chart.png

// after
let abs = workspace_dir.join(&rel_path);
let msg = format!("[image:{}]", abs.display());
Defensive patterns

Strategy: validation

Validate before calling

fn build_attachment_marker(kind: &str, workspace_dir: &Path, rel: impl AsRef<Path>) -> String {
    let abs = workspace_dir.join(rel); // markers must carry absolute paths
    format!("[{kind}:{}]", abs.display())
}

Type guard

fn marker_target_is_absolute(target: &str) -> bool {
    std::path::Path::new(target).is_absolute()
}

Prevention

When it happens

Trigger: An outbound attachment marker carries a relative path ('uploads/x.png', 'chart.png'); the check at slack.rs:1036 bails before canonicalization and the workspace containment test.

Common situations: Agents emitting paths relative to the workspace root because that reads more naturally; code building markers from PathBuf values that were joined relatively; copying example marker syntax with a shortened path.

Related errors


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