tinyhumansai/openhuman · warning · anyhow::Error

config_get http {}

Error message

config_get http {}

What it means

fetch_imessage_gate() POSTs openhuman.config_get to the core's /rpc (auth applied via core_rpc::apply_auth) to read channels_config.imessage before a scan tick (called from tick.rs); a non-success HTTP status bails with `config_get http <status>`. The scan's SQLite side is fine — this is shell→core communication failing.

Source

Thrown at app/src-tauri/src/imessage_scanner/mod.rs:208

///
/// Returns:
/// - `Ok(Some(allowed_contacts))` when iMessage is connected (allow-list may
///   be empty = "all chats")
/// - `Ok(None)` when iMessage is not connected / config absent
/// - `Err(_)` on transport or parse errors (caller should retry next tick)
#[cfg(target_os = "macos")]
async fn fetch_imessage_gate() -> anyhow::Result<Option<Vec<String>>> {
    let url = crate::core_rpc::core_rpc_url_value();
    let body = json!({
        "jsonrpc": "2.0",
        "id": 1,
        "method": "openhuman.config_get",
        "params": {}
    });
    let req = crate::core_rpc::apply_auth(http_client().post(&url)).map_err(anyhow::Error::msg)?;
    let res = req.json(&body).send().await?;
    if !res.status().is_success() {
        anyhow::bail!("config_get http {}", res.status());
    }
    let v: serde_json::Value = res.json().await?;
    // JSON-RPC envelope is `{"result": {"logs": [...], "result": <RpcOutcome body>}}`
    // so the config lives at `/result/result/config/...`, not `/result/config/...`.
    let imessage = v
        .pointer("/result/result/config/channels_config/imessage")
        .cloned();
    let Some(imessage) = imessage else {
        return Ok(None);
    };
    if imessage.is_null() {
        return Ok(None);
    }
    let contacts = imessage
        .get("allowed_contacts")
        .and_then(|c| c.as_array())
        .map(|arr| {
            arr.iter()

View on GitHub (pinned to a221052e0d)

Solutions

  1. Restart the app so the shell re-hands the fresh in-memory bearer to the scanner
  2. If pointing at an external core, make sure the token matches that core (OPENHUMAN_CORE_TOKEN / core.token)
  3. Check core logs at the timestamp to see the actual status and body
  4. Treat it as transient: ticks are periodic and self-heal on the next cycle once auth is aligned

Example fix

// before (imessage_scanner/tick.rs)
let gate = super::fetch_imessage_gate().await?;   // aborts the whole tick

// after — degrade this optional-feature fetch instead of failing the tick
let gate = match super::fetch_imessage_gate().await {
    Ok(g) => g,
    Err(e) => { log::warn!("[imessage] gate unavailable ({e}); skipping tick"); return Ok(()); }
};
Defensive patterns

Strategy: retry

Validate before calling

async fn core_healthy(base: &str) -> bool {
    let health = format!("{}/health", base.trim_end_matches("/rpc"));
    crate::core_rpc::apply_auth(http_client().get(health)).send().await
        .map(|r| r.status().is_success()).unwrap_or(false)
}
// skip the gate fetch (and the scan) when the core is mid-restart

Try / catch

let gate = match fetch_imessage_gate().await {
    Ok(g) => g,
    Err(e) => {
        log::warn!("[imessage] gate fetch failed ({e}); retrying next tick");
        return Ok(());   // periodic tick replays this for free
    }
};

Prevention

When it happens

Trigger: 401 from a stale per-launch bearer (the core restarted and got a new in-memory token while the scanner tick was in flight); core mid-shutdown during app update; connection-level failure surfaced as a status error; 500 from the core while serializing config.

Common situations: Core auto-restarted after an update while a tick ran; debugging with OPENHUMAN_CORE_REUSE_EXISTING=1 against a core started with a different token; core crashed and the shell has not noticed yet.

Related errors


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