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
- Use absolute paths in attachment markers
- Verify the file-generation step completed (and logged success) before the send step runs
- 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
- Use absolute paths in every attachment marker produced by agents/pipelines
- Order steps so file generation completes (and is verified) before the send step
- For /tmp-based artifacts, send immediately after generation or move them to a stable workspace directory
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
- QQ upload media failed ({status}): {err}
- QQ send media message failed ({status}): {err}
- QQ attachment too large ({} bytes, max {}): {target}
- Discord send message with files failed ({status}): {err}
- Messages database not found at {}. Ensure Messages.app is se
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/cb23b4a41a62f35b.
Report an issue: GitHub.