{"record":{"id":"dcceb18a348fc3a3","repo":"zeroclaw-labs/zeroclaw","slug":"telegram-attachment-path-not-found-target","errorCode":null,"errorMessage":"Telegram attachment path not found: {target}","messagePattern":"Telegram attachment path not found: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/telegram.rs","lineNumber":3127,"sourceCode":"\n        // Remap Docker container workspace path (/workspace/...) to the host\n        // workspace directory so files written by the containerised runtime\n        // can be found and sent by the host-side Telegram sender.\n        let remapped;\n        let target = if let Some(rel) = target.strip_prefix(\"/workspace/\") {\n            if let Some(ws) = &self.workspace_dir {\n                remapped = ws.join(rel);\n                remapped.to_str().unwrap_or(target)\n            } else {\n                target\n            }\n        } else {\n            target\n        };\n\n        let path = Path::new(target);\n        if !path.exists() {\n            anyhow::bail!(\"Telegram attachment path not found: {target}\");\n        }\n\n        match attachment.kind {\n            TelegramAttachmentKind::Image => self.send_photo(chat_id, thread_id, path, None).await,\n            TelegramAttachmentKind::Document => {\n                self.send_document(chat_id, thread_id, path, None).await\n            }\n            TelegramAttachmentKind::Video => self.send_video(chat_id, thread_id, path, None).await,\n            TelegramAttachmentKind::Audio => self.send_audio(chat_id, thread_id, path, None).await,\n            TelegramAttachmentKind::Voice => self.send_voice(chat_id, thread_id, path, None).await,\n        }\n    }\n\n    /// Send a document/file to a Telegram chat\n    pub async fn send_document(\n        &self,\n        chat_id: &str,\n        thread_id: Option<&str>,","sourceCodeStart":3109,"sourceCodeEnd":3145,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/telegram.rs#L3109-L3145","documentation":"Before uploading an attachment, send_attachment remaps Docker-style /workspace/... paths onto the configured host workspace_dir, then requires the resulting path to exist; otherwise it bails with the (possibly still-remapped) target. This is a host/container filesystem mismatch or missing-file guard, not a Telegram API error.","triggerScenarios":"Containerized runtime wrote /workspace/artifact.png but workspace_dir is None on the host-side sender, so the /workspace/... path is kept and does not exist on the host; the artifact was deleted (or not yet flushed) between generation and send; workspace_dir points at a different directory than the container volume; relative paths resolved against the sender's cwd.","commonSituations":"Docker deployment missing the volume mount or the workspace_dir setting; artifacts written to a temp dir that a cleaner reaps before the async send runs; host/container layout drift after moving the workspace.","solutions":["Check the printed target: if it still starts with /workspace/, the remap never happened — configure the channel's workspace_dir to the host directory mounted at the container's /workspace.","Add the matching volume mount (e.g. -v ./workspace:/workspace) so both sides see the same files.","Verify with `ls` that the file exists at that exact host path and that the producer finished writing before the send fires.","Have artifact generators emit absolute paths rooted in the shared workspace."],"exampleFix":"# before — container writes /workspace/out.png, host sender has no workspace_dir\n# send fails: Telegram attachment path not found: /workspace/out.png\n\n# after — share the directory and configure the remap\ndocker run -v \"$PWD/workspace:/workspace\" ...\n# and set the telegram channel's workspace_dir to \"$PWD/workspace\"","handlingStrategy":"validation","validationCode":"// resolve the same way the channel does, then verify before sending\nlet target = if let Some(rel) = raw.strip_prefix(\"/workspace/\") {\n    workspace_dir.join(rel).to_string_lossy().into_owned()\n} else {\n    raw.to_string()\n};\nanyhow::ensure!(std::path::Path::new(&target).exists(), \"attachment missing before send: {target}\");","typeGuard":"fn attachment_resolves(raw: &str, workspace_dir: Option<&Path>) -> bool {\n    let p = match raw.strip_prefix(\"/workspace/\") {\n        Some(rel) => match workspace_dir { Some(ws) => ws.join(rel), None => return false },\n        None => PathBuf::from(raw),\n    };\n    p.exists()\n}","tryCatchPattern":"if let Err(e) = channel.send_attachment(chat, &attachment).await {\n    if e.to_string().contains(\"attachment path not found\") {\n        tracing::warn!(\"artifact vanished; notifying user\");\n        return channel.send_text_chunks(\"attachment unavailable\", chat, thread).await;\n    }\n    return Err(e);\n}","preventionTips":["Mount the container workspace into the host (-v $PWD/workspace:/workspace) and set the channel's workspace_dir to the same host path.","Generate artifacts inside the shared workspace only, with absolute paths.","Wait for artifact writers to finish (flush/rename on write) before triggering the send."],"tags":["telegram","attachment","docker","filesystem","path-mapping"],"backgroundTag":"attachment-file-not-found","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}