zeroclaw-labs/zeroclaw · error · anyhow::Error
Lark/Feishu marker target uses a disallowed scheme
Error message
Lark/Feishu marker target uses a disallowed scheme
What it means
validate_lark_marker_target lowercases the target and rejects any string starting with http://, https://, data: or file:, or containing '://' at all — media markers must reference local files, never URLs or inline data. The guard exists because the resolved path is later uploaded to Lark, and URL targets would either break path resolution or smuggle remote content; the WARN log records reason=disallowed_scheme.
Source
Thrown at crates/zeroclaw-channels/src/lark.rs:591
let trimmed = target.trim();
if trimmed.is_empty() {
anyhow::bail!("Lark/Feishu marker target is empty");
}
let lower = trimmed.to_ascii_lowercase();
if lower.starts_with("http://")
|| lower.starts_with("https://")
|| lower.starts_with("data:")
|| lower.starts_with("file:")
|| lower.contains("://")
{
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
.with_attrs(::serde_json::json!({"reason": "disallowed_scheme"})),
"lark: marker target uses disallowed scheme"
);
anyhow::bail!("Lark/Feishu marker target uses a disallowed scheme");
}
let workspace = workspace_dir.ok_or_else(|| {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
.with_attrs(::serde_json::json!({"reason": "no_workspace_dir"})),
"lark: local marker target has no workspace_dir"
);
anyhow::Error::msg("Lark/Feishu channel was started without a workspace_dir")
})?;
let workspace = std::fs::canonicalize(workspace).map_err(|err| {
anyhow::Error::msg(format!(
"canonicalize Lark/Feishu workspace_dir failed: {err}"
))
})?;
let candidate = Path::new(trimmed);View on GitHub (pinned to 88bb9c8533)
Solutions
- Download the URL into workspace_dir first (with your own fetch step), then emit a marker with the workspace-relative path
- For data: URIs, decode and write the bytes to a file inside the workspace, then reference that file
- Adjust the agent instructions so media markers always carry a path under the workspace, never a link
Example fix
// before — marker target is a URL
{ "kind": "image", "target": "https://cdn.example.com/cat.png" }
// after — fetch to workspace, then reference the local file
let path = fetch_to_workspace("https://cdn.example.com/cat.png").await?;
{ "kind": "image", "target": path.to_string_lossy() } Defensive patterns
Strategy: validation
Validate before calling
// Reject URL/scheme targets before sending:
fn is_local_marker_target(t: &str) -> bool {
let l = t.trim().to_ascii_lowercase();
!l.is_empty() && !l.contains("://") && !l.starts_with("data:") && !l.starts_with("file:")
} Try / catch
if let Err(e) = channel.send(&msg).await {
if e.to_string().contains("disallowed scheme") {
// fetch the URL into workspace_dir yourself, replace target with the local path, retry once
}
} Prevention
- Teach the agent/tool contract: media markers carry workspace-relative file paths only
- Pipe any URL-based attachment through an explicit download-to-workspace step
When it happens
Trigger: An outgoing media marker carries target = "https://cdn.example.com/img.png", "data:image/png;base64,...", "file:///Users/x/a.png", or any scheme-bearing string; resolve_lark_media_marker -> validate_lark_marker_target rejects it before workspace resolution. The tests lark_marker_target_rejects_url_schemes pin this behaviour.
Common situations: The model pastes a URL instead of producing a local file; a tool returns links rather than writing artifacts into the workspace; prompt examples demonstrate URL-based attachments.
Related errors
- Lark/Feishu marker target is empty
- Lark/Feishu marker target resolves outside workspace_dir
- Lark/Feishu marker target is not a file
- Edge TTS binary_path must be a bare command name without pat
- security.otp.cache_valid_secs must be greater than or equal
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/d4da77250a60240a.
Report an issue: GitHub.