tinyhumansai/openhuman · error

{tool}: re-read failed: {e:#}

Error message

{tool}: re-read failed: {e:#}

What it means

Thrown by set_pin when the pin/unpin UPDATE succeeded but the immediate re-read (FacetCache::get) of the same key failed. A rare storage failure between two statements in one tool call: the pin IS persisted, only the confirmation read failed. Same causes as the other store errors — lock transition, I/O error, DB closing underneath.

Source

Thrown at src/openhuman/agent/learning/tools.rs:279

/// Set/clear a facet's pin via `set_user_state`. Shared by pin/unpin.
async fn set_pin(
    args: serde_json::Value,
    tool: &str,
    state: UserState,
) -> anyhow::Result<ToolResult> {
    let class_str = read_required_str(&args, "class")?;
    let key_suffix = read_required_str(&args, "key")?;
    let fk = full_key(&class_str, &key_suffix);
    let cache = get_cache()?;
    let updated = cache
        .set_user_state(&fk, state)
        .map_err(|e| anyhow::anyhow!("{tool}: set_user_state failed: {e:#}"))?;
    if !updated {
        return Err(anyhow::anyhow!("{tool}: facet not found: {fk}"));
    }
    let facet = cache
        .get(&fk)
        .map_err(|e| anyhow::anyhow!("{tool}: re-read failed: {e:#}"))?;
    Ok(ToolResult::success(serde_json::to_string(&json!({
        "facet": facet.as_ref().map(facet_to_json),
    }))?))
}

/// Pin a facet. Default-OFF.
pub struct LearningPinFacetTool;

#[async_trait]
impl Tool for LearningPinFacetTool {
    fn name(&self) -> &str {
        "learning_pin_facet"
    }

    fn description(&self) -> &str {
        "Pin a learned facet (`class` + `key`) so it stays active regardless of \
         the stability detector."
    }

View on GitHub (pinned to a221052e0d)

Solutions

  1. Treat the pin as probably applied — verify with learning_get_facet instead of blindly re-pinning
  2. Retry the whole pin/unpin if verification shows the old user_state — the operation is idempotent
  3. Avoid issuing learning tools while the core is restarting
Defensive patterns

Strategy: retry

Try / catch

match tool_exec(&tool_name, args).await {
    Err(e) if e.to_string().contains("re-read failed") => {
        // the pin itself likely landed — confirm instead of blind retry
        tool_exec("learning_get_facet", args).await // inspect user_state
    }
    other => other,
}

Prevention

When it happens

Trigger: Another connection takes the write lock between the UPDATE and the SELECT (e.g. a rebuild cycle starting mid-call); the memory client's store being shut down concurrently (core shutting down mid-tool); disk I/O error.

Common situations: Core shutdown or restart racing an in-flight pin tool call; aggressive scheduled stability cycles in small workspaces.

Related errors


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