tinyhumansai/openhuman · error

integration id is required

Error message

integration id is required

What it means

Thrown by revoke_integration when the integration id is empty after trimming. The revoke is a DELETE on auth/integrations/{id}; unlike the handoff/client-key methods there is no 24-char length requirement here — only non-emptiness — but the id is still the backend integration identifier. Input validation only.

Source

Thrown at src/api/rest.rs:1121

    ) -> Result<Value> {
        let channel = channel.trim().trim_matches('/');
        anyhow::ensure!(!channel.is_empty(), "channel is required");
        let encoded = urlencoding::encode(channel);
        let mut path = format!("channels/{encoded}/threads");
        if let Some(active) = active_filter {
            path.push_str(if active {
                "?active=true"
            } else {
                "?active=false"
            });
        }
        self.authed_json(bearer_jwt, Method::GET, &path, None).await
    }

    /// Revokes (deletes) an active integration.
    pub async fn revoke_integration(&self, integration_id: &str, bearer_jwt: &str) -> Result<()> {
        let id = integration_id.trim();
        anyhow::ensure!(!id.is_empty(), "integration id is required");
        self.authed_json(
            bearer_jwt,
            Method::DELETE,
            &format!("auth/integrations/{id}"),
            None,
        )
        .await?;
        Ok(())
    }
}

/// AES-256-GCM decrypt compatible with backend `encryptMessageFromString` (IV 16 + tag 16 + ciphertext, base64).
pub fn decrypt_handoff_blob(b64_ciphertext: &str, key_str: &str) -> Result<String> {
    let key = key_bytes_from_string(key_str)?;
    let combined = base64::engine::general_purpose::STANDARD
        .decode(b64_ciphertext.trim())
        .context("base64-decode encrypted payload")?;
    if combined.len() < 32 {

View on GitHub (pinned to a221052e0d)

Solutions

  1. Pass the id verbatim from list_integrations
  2. Disable revoke actions on rows without an id
  3. Guard at the call site with trim()

Example fix

// before
client.revoke_integration(&integration_id, &jwt).await?; // integration_id = ""

// after
let id = integration_id.trim();
anyhow::ensure!(!id.is_empty(), "integration id is required to revoke");
client.revoke_integration(id, &jwt).await?;
Defensive patterns

Strategy: validation

Validate before calling

let id = integration_id.trim();
anyhow::ensure!(!id.is_empty(), "integration id is required to revoke");
client.revoke_integration(id, &jwt).await?;

Type guard

fn has_integration_id(s: &str) -> bool {
    !s.trim().is_empty()
}

Prevention

When it happens

Trigger: Calling revoke_integration("") — e.g. a revoke action dispatched from a UI row whose id failed to load, or a stale record with a blank id field.

Common situations: Settings screen's revoke button enabled on a row rendered from partial data, or an id lost through serialization after a refactor.

Related errors


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