tinyhumansai/openhuman · error · anyhow::Error
artifact_get: {e}
Error message
artifact_get: {e} What it means
Wrapper around `ops::ai_get_artifact` failing in ArtifactGetTool::execute after `read_required_str` already validated `artifact_id`. The dominant inner failure is store::get_artifact's "artifact not found or unreadable id={id}" (store.rs:242): no `<workspace>/artifacts/<id>/meta.json`, or the file exists but is unreadable/unparseable. It also covers an artifacts_root creation failure.
Source
Thrown at src/openhuman/agent/artifacts/tools.rs:139
first to discover ids."
}
fn parameters_schema(&self) -> serde_json::Value {
json!({
"type": "object",
"properties": {
"artifact_id": { "type": "string", "description": "The artifact id (UUID) to fetch." }
},
"required": ["artifact_id"]
})
}
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
log::debug!("[tool][artifacts] get invoked");
let id = read_required_str(&args, "artifact_id")?;
let outcome = ops::ai_get_artifact(&self.config, &id)
.await
.map_err(|e| anyhow::anyhow!("artifact_get: {e}"))?;
Ok(ToolResult::success(serde_json::to_string(&outcome.value)?))
}
fn is_concurrency_safe(&self, _args: &serde_json::Value) -> bool {
true
}
}
/// Delete an artifact directory and all its contents. **Irreversible** —
/// ships default-OFF (`Dangerous`).
pub struct ArtifactDeleteTool {
config: Arc<Config>,
}
impl ArtifactDeleteTool {
pub fn new(config: Arc<Config>) -> Self {
Self { config }
}View on GitHub (pinned to a221052e0d)
Solutions
- Call artifact_list first and use an id that is currently present.
- Confirm the id is the full UUID returned in the listing, not a truncated prefix.
- Verify the same workspace: ids are scoped to `<workspace>/artifacts/`, so a changed workspace_dir invalidates old ids.
- If meta.json is corrupted, remove the artifact dir via artifact_delete (or manually) and regenerate.
- Check filesystem permissions on `<workspace>/artifacts/<id>/`.
Example fix
# before
{ "artifact_id": "550e8400" } # truncated/hallucinated -> not found
# after
# 1) artifact_list -> take meta.id
{ "artifact_id": "550e8400-e29b-41d4-a716-446655440000" } Defensive patterns
Strategy: validation
Validate before calling
// Resolve the id from a live listing before calling get:
let listing = ops::ai_list_artifacts(&config, 0, 500, None).await?;
let known: HashSet<String> = listing.value.artifacts.iter().map(|a| a.id.clone()).collect();
if !known.contains(&wanted_id) {
anyhow::bail!("artifact {wanted_id} not in current workspace listing");
} Type guard
fn is_plausible_artifact_id(s: &str) -> bool {
// ids are UUIDs persisted as <workspace>/artifacts/<id>/meta.json
s.len() == 36 && s.matches('-').count() == 4
} Try / catch
match ops::ai_get_artifact(&config, &id).await {
Ok(meta) => meta,
Err(e) if e.to_string().contains("not found or unreadable") => {
// stale/hallucinated id: re-list, pick by title, or tell the model the artifact is gone
}
Err(e) => return Err(anyhow!("artifact_get: {e}")),
} Prevention
- Always source ids from artifact_list in the same workspace.
- Remember ids are workspace-scoped — changing workspace_dir invalidates them.
- Treat 'not found or unreadable' as a cache miss: re-list and retry once.
When it happens
Trigger: Agent calls artifact_get with a hallucinated or truncated UUID; the artifact directory was deleted out-of-band (user cleaned artifacts/, another delete raced); meta.json is corrupted (partial write) or permissions deny the read.
Common situations: Model reusing an id from an earlier session whose workspace differs (workspace_dir changed via --workspace/env); concurrent artifact_delete; disk-full during a previous meta.json write leaving invalid JSON.
Related errors
- artifact_list: {e}
- artifact_delete: {e}
- missing required string argument `{key}`
- {tool}: facet not found: {fk}
- toolkit `{toolkit}` is not connected. Connected toolkits: [{
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/e6dc3d8417203471.
Report an issue: GitHub.