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

  1. Call artifact_list first and use an id that is currently present.
  2. Confirm the id is the full UUID returned in the listing, not a truncated prefix.
  3. Verify the same workspace: ids are scoped to `<workspace>/artifacts/`, so a changed workspace_dir invalidates old ids.
  4. If meta.json is corrupted, remove the artifact dir via artifact_delete (or manually) and regenerate.
  5. 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

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


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/e6dc3d8417203471. Report an issue: GitHub.