tinyhumansai/openhuman · error

learning_rebuild_cache: rebuild failed: {e:#}

Error message

learning_rebuild_cache: rebuild failed: {e:#}

What it means

Thrown by learning_rebuild_cache when StabilityDetector::rebuild(now) fails. rebuild() recomputes facet stability from the accumulated evidence/candidate buffer and rewrites the user_profile table (adding, evicting, and keeping rows, respecting class budgets and pin protection) — a multi-row write cycle, so any store error mid-cycle aborts the whole rebuild. It is the heavyweight stability cycle, default-OFF alongside the other mutators.

Source

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

    fn parameters_schema(&self) -> serde_json::Value {
        json!({ "type": "object", "properties": {} })
    }

    fn permission_level(&self) -> PermissionLevel {
        PermissionLevel::Execute
    }

    async fn execute(&self, _args: serde_json::Value) -> anyhow::Result<ToolResult> {
        log::debug!("[tool][learning] rebuild_cache invoked");
        let cache = get_cache()?;
        let detector = StabilityDetector::new(cache);
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs_f64())
            .unwrap_or(0.0);
        let outcome = detector
            .rebuild(now)
            .map_err(|e| anyhow::anyhow!("learning_rebuild_cache: rebuild failed: {e:#}"))?;
        Ok(ToolResult::success(serde_json::to_string(&json!({
            "added": outcome.added,
            "evicted": outcome.evicted,
            "kept": outcome.kept,
            "total_size": outcome.total_size,
        }))?))
    }
}

/// Reset the facet cache (delete all auto facets, keep pinned). Default-OFF.
pub struct LearningResetCacheTool;

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

View on GitHub (pinned to a221052e0d)

Solutions

  1. Ensure no other learning mutator is in flight, then retry the rebuild — it is designed as a full recompute, so a partial outcome is safe to redo
  2. Check learning_cache_stats afterwards to see the resulting row counts
  3. Free disk space / verify the workspace DB if the error chain shows I/O rather than lock errors
  4. If it fails deterministically, capture the {e:#} chain — a deserialization error on a specific row points at data corruption needing a reset_cache
Defensive patterns

Strategy: try-catch

Validate before calling

// Rebuild is a full recompute — safe to re-run, but check store health first:
tool_exec("learning_cache_stats", json!({})).await?; // errors => defer the rebuild

Try / catch

match tool_exec("learning_rebuild_cache", json!({})).await {
    Ok(out) => log::info!("rebuild added/evicted/kept: {out}"),
    Err(e) => {
        log::warn!("rebuild failed: {e:#}");
        // leave cache as-is; it is still the last consistent snapshot — retry later when idle
    }
}

Prevention

When it happens

Trigger: Invoking learning_rebuild_cache while pin/update/forget/reset tools or reflection persistence write the same table (lock contention aborts the transaction); disk full partway through the rewrite; corrupted evidence/facet rows tripping deserialization inside the detector.

Common situations: Agent-triggered rebuild overlapping a scheduled stability cycle; rebuild on a nearly-full disk leaving the cache partially rewritten; concurrent UI learning RPC calls.

Related errors


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