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

Slack outbound attachment target is not a file: {}

Error message

Slack outbound attachment target is not a file: {}

What it means

Raised by SlackChannel::resolve_outbound_attachment_marker when tokio::fs::metadata succeeds on the canonicalized target but metadata.is_file() is false — the target is a directory, fifo, device node, or other non-regular file. It fires after the workspace containment check and before the size check against SLACK_OUTBOUND_ATTACHMENT_MAX_BYTES (20 MiB), so the marker target must be a readable regular file inside the workspace.

Source

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

            .with_context(|| format!("Slack outbound attachment path not found: {target}"))?;

        if !canonical_path.starts_with(&canonical_workspace) {
            anyhow::bail!(
                "Slack outbound attachment path escapes workspace: {}",
                canonical_path.display()
            );
        }

        let metadata = tokio::fs::metadata(&canonical_path)
            .await
            .with_context(|| {
                format!(
                    "failed to stat Slack outbound attachment {}",
                    canonical_path.display()
                )
            })?;
        if !metadata.is_file() {
            anyhow::bail!(
                "Slack outbound attachment target is not a file: {}",
                canonical_path.display()
            );
        }
        if metadata.len() > SLACK_OUTBOUND_ATTACHMENT_MAX_BYTES as u64 {
            anyhow::bail!(
                "Slack outbound attachment exceeds {} bytes: {}",
                SLACK_OUTBOUND_ATTACHMENT_MAX_BYTES,
                canonical_path.display()
            );
        }

        let data = tokio::fs::read(&canonical_path).await.with_context(|| {
            format!(
                "failed to read Slack outbound attachment {}",
                canonical_path.display()
            )
        })?;

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Point the marker at a specific regular file inside the workspace
  2. If the intent was 'attach everything in this directory', enumerate the files and emit one marker per file
  3. Generate the asset fully (close/flush writers) before the marker is resolved
Defensive patterns

Strategy: validation

Validate before calling

async fn attachment_is_sendable(target: &str) -> bool {
    match tokio::fs::metadata(target).await {
        Ok(m) => m.is_file() && m.len() <= 20 * 1024 * 1024, // regular file, <= SLACK_OUTBOUND_ATTACHMENT_MAX_BYTES
        Err(_) => false,
    }
}

Prevention

When it happens

Trigger: An outbound attachment marker points at a directory (e.g. [file:/workspace/reports]) or a special file; the stat succeeds, is_file() is false, and the bail includes the canonical path.

Common situations: Agents referencing a folder of exports instead of a specific file; markers generated from a path where the file was expected but a directory was created; trailing-slash or empty-name paths resolving to a directory.

Related errors


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