zeroclaw-labs/zeroclaw · error

QQ attachment path not found: {target}

Error message

QQ attachment path not found: {target}

What it means

send_attachment treats any target that is not an http(s) URL as a local file path and bails when Path::exists() is false, before any size check or upload. The target comes from attachment markers embedded in agent output, e.g. [DOCUMENT:/path/to/report.pdf] or [IMAGE:/tmp/chart.png].

Source

Thrown at crates/zeroclaw-channels/src/qq.rs:970

        if target.starts_with("http://") || target.starts_with("https://") {
            // URL upload — no caching (remote content may change)
            let (file_info, _ttl) = self
                .upload_media(
                    recipient,
                    attachment.kind,
                    Some(target),
                    None,
                    file_name.as_deref(),
                )
                .await?;
            self.send_media_message(recipient, &file_info, in_reply_to)
                .await?;
        } else {
            // Local file upload
            let path = Path::new(target);
            if !path.exists() {
                anyhow::bail!("QQ attachment path not found: {target}");
            }

            let metadata = tokio::fs::metadata(path).await?;
            if metadata.len() > QQ_MAX_UPLOAD_BYTES {
                anyhow::bail!(
                    "QQ attachment too large ({} bytes, max {}): {target}",
                    metadata.len(),
                    QQ_MAX_UPLOAD_BYTES
                );
            }

            let file_bytes = tokio::fs::read(path).await?;
            let (scope_label, target_id) = Self::resolve_recipient(recipient);
            let scope = if scope_label == "groups" {
                "group"
            } else {
                "c2c"
            };

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Use absolute paths in attachment markers
  2. Verify the file-generation step completed (and logged success) before the send step runs
  3. Fix typos/case in the path — the error message includes the exact target that was checked

Example fix

# before
Report ready: [DOCUMENT:output/report.pdf]     # relative path, wrong cwd -> bail

# after
Report ready: [DOCUMENT:/var/lib/zeroclaw/work/report.pdf]
Defensive patterns

Strategy: validation

Validate before calling

// Validate every local attachment target before calling send:
async fn attachments_exist(targets: &[String]) -> anyhow::Result<()> {
    for t in targets {
        if !t.starts_with("http://") && !t.starts_with("https://") {
            anyhow::ensure!(Path::new(t).exists(), "attachment path not found: {t}");
        }
    }
    Ok(())
}

Type guard

fn is_local_missing_target(target: &str) -> bool {
    !(target.starts_with("http://") || target.starts_with("https://"))
        && !Path::new(target).exists()
}

assert!(is_local_missing_target("/no/such/file.png"));
assert!(!is_local_missing_target("https://example.com/a.png"));

Try / catch

match ch.send(&msg).await {
    Err(e) if e.to_string().starts_with("QQ attachment path not found") => {
        // Deterministic local-state problem: extract the path from the error, regenerate
        // or remove the marker, then resend; never retry unchanged.
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: A marker references a file that was never created, was already deleted, or whose path is wrong — including relative paths resolved against the process working directory rather than the intended one.

Common situations: Agent steps that generate a file in one workspace and reference it with a path from another; /tmp cleanup between generation and send; relative paths in markers while the daemon runs from a different cwd; tilde paths that are not expanded.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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