{"record":{"id":"4f51c8cd0575b0a8","repo":"zeroclaw-labs/zeroclaw","slug":"lark-feishu-marker-target-is-not-a-file","errorCode":null,"errorMessage":"Lark/Feishu marker target is not a file","messagePattern":"Lark/Feishu marker target is not a file","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/lark.rs","lineNumber":656,"sourceCode":"        );\n        anyhow::bail!(\"Lark/Feishu marker target resolves outside workspace_dir\");\n    }\n\n    Ok(candidate)\n}\n\nfn resolve_lark_media_marker(\n    marker: &LarkOutgoingMediaMarker,\n    workspace_dir: Option<&Path>,\n) -> anyhow::Result<LarkResolvedMediaMarker> {\n    let path = validate_lark_marker_target(&marker.target, workspace_dir)?;\n    let metadata = std::fs::metadata(&path).map_err(|err| {\n        anyhow::Error::msg(format!(\n            \"read Lark/Feishu marker target metadata failed: {err}\"\n        ))\n    })?;\n    if !metadata.is_file() {\n        anyhow::bail!(\"Lark/Feishu marker target is not a file\");\n    }\n    if metadata.len() == 0 {\n        anyhow::bail!(\"Lark/Feishu marker target is empty\");\n    }\n    let file_name = path\n        .file_name()\n        .and_then(|name| name.to_str())\n        .unwrap_or(\"attachment\")\n        .to_string();\n\n    Ok(LarkResolvedMediaMarker {\n        kind: marker.kind,\n        path,\n        file_name,\n    })\n}\n\nasync fn build_lark_image_upload_form(marker: &LarkResolvedMediaMarker) -> anyhow::Result<Form> {","sourceCodeStart":638,"sourceCodeEnd":674,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/lark.rs#L638-L674","documentation":"resolve_lark_media_marker stats the validated path and requires metadata.is_file() — the marker target must resolve to a regular file, not a directory, fifo, socket or device. All scheme/workspace containment checks already passed by this point, so this error specifically means: the path exists, is inside the workspace, but is not something uploadable as image/file content.","triggerScenarios":"A marker points at a directory (\"screenshots/\" or \"attachments\" without a filename) or at a special file; std::fs::metadata succeeds, is_file() is false, and send/finalize_draft bail before reading bytes or building the upload form.","commonSituations":"Agent truncates a path to its folder; tool writes to a directory and passes the folder as the artifact; marker built by joining a base dir while the filename component was empty.","solutions":["Point the marker at the concrete file including its filename (\"screenshots/run-42.png\", not \"screenshots/\")","Have the file-producing tool return the exact artifact path it wrote and feed that verbatim into the marker","If attaching multiple files, emit one marker per file rather than one marker per directory"],"exampleFix":"// before\n{ \"kind\": \"image\", \"target\": \"reports/daily\" }   // a directory\n\n// after\n{ \"kind\": \"image\", \"target\": \"reports/daily/chart.png\" }","handlingStrategy":"validation","validationCode":"// Before sending, confirm the marker resolves to a regular file:\nlet p = std::fs::canonicalize(workspace.join(target.trim()))?;\nanyhow::ensure!(std::fs::metadata(&p)?.is_file(), \"marker target is not a regular file: {p:?}\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = channel.send(&msg).await {\n    if e.to_string().contains(\"is not a file\") {\n        // append the concrete filename to the target and retry\n    }\n}","preventionTips":["Always emit file paths with the filename component — never a bare directory","Return exact artifact paths from tools and pass them through unmodified"],"tags":["lark","media-marker","filesystem","upload"],"backgroundTag":"not-a-regular-file","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}