tinyhumansai/openhuman · error

API request failed

Error message

API request failed: {message}

What it means

authed_json unwraps the TinyHumans backend's { success, data, user } envelope; when the parsed JSON body has success=false it bails with 'API request failed: {message}', taking the body's message or error field ('request unsuccessful' when neither exists). It means the backend received and answered the request with a business-level failure - distinct from transport errors and from the separately-handled 401/404 statuses.

Solutions

  1. Inspect the embedded {message} - it is the backend's own failure text and usually names the exact problem.
  2. Verify the request payload against the current vendored SDK route definition (the SDK is the source of truth for routes and params).
  3. Check for version skew between the app, the vendored SDK submodule, and the backend.
  4. If the message smells like auth but the call got this far, remember real 401 session expiry is classified separately - this path is a body-level rejection.
Defensive patterns

Strategy: try-catch

Try / catch

In Rust callers, match on the anyhow error: if the message starts with 'API request failed:', surface the remainder (the backend's message) to the user as a validation or entitlement problem; otherwise propagate. When using SDK-typed routes, prefer classify_sdk_error so Sentry and session-expiry behavior stay consistent.

Prevention

When it happens

Trigger: Any SDK-backed authed_json call whose backend handler responds 2xx-wrapped (or 200-with) success=false: invalid or newly-required parameters, entitlement or plan restrictions, semantically rejected payloads, or account-level feature flags.

Common situations: A backend rollout tightened validation and the client still sends the old payload shape; a newly required field was missed; the account lacks an entitlement the route checks.

Related errors


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

Appendix: source

Thrown at src/api/rest.rs:311

/// than `{success,data}`; SDK transport must not expose that envelope detail to
/// existing callers.
fn parse_api_response_value(value: Value) -> Result<Value> {
    let Some(object) = value.as_object() else {
        return Ok(value);
    };
    if let Some(user) = object.get("user").filter(|user| !user.is_null()) {
        return Ok(user.clone());
    }
    let Some(success) = object.get("success").and_then(Value::as_bool) else {
        return Ok(value);
    };
    if !success {
        let message = object
            .get("message")
            .or_else(|| object.get("error"))
            .and_then(Value::as_str)
            .unwrap_or("request unsuccessful");
        anyhow::bail!("API request failed: {message}");
    }
    if let Some(data) = object.get("data").filter(|data| !data.is_null()) {
        return Ok(data.clone());
    }
    if let Some(user) = object.get("user").filter(|user| !user.is_null()) {
        return Ok(user.clone());
    }
    let mut unwrapped = object.clone();
    unwrapped.remove("success");
    Ok(Value::Object(unwrapped))
}

fn user_id_from_object(obj: &serde_json::Map<String, Value>) -> Option<String> {
    for key in ["id", "_id", "userId"] {
        if let Some(s) = obj.get(key).and_then(|x| x.as_str()) {
            let t = s.trim();
            if !t.is_empty() {
                return Some(t.to_string());

View on GitHub (pinned to 7491200858)