zeroclaw-labs/zeroclaw · error

LinkedIn get_engagement failed ({}): {}

Error message

LinkedIn get_engagement failed ({}): {}

What it means

Thrown when GET of a post's engagement statistics returns non-2xx, body included. Engagement endpoints are restricted: tokens typically need the correct product/scope, and organizational analytics require additional permissions. The parsed JSON is then shaped into an EngagementSummary, so shape problems surface after this check.

Source

Thrown at crates/zeroclaw-tools/src/linkedin_client.rs:457

            let body_text = response.text().await.unwrap_or_default();
            anyhow::bail!("LinkedIn delete_post failed ({}): {}", status, body_text);
        }

        Ok(())
    }

    pub async fn get_engagement(&self, post_id: &str) -> anyhow::Result<EngagementSummary> {
        let creds = self.get_credentials().await?;
        let url = format!("{}/rest/socialActions/{}", LINKEDIN_API_BASE, post_id);

        let response = self
            .api_request(Method::GET, &url, &creds.access_token, None)
            .await?;

        let status = response.status();
        if !status.is_success() {
            let body_text = response.text().await.unwrap_or_default();
            anyhow::bail!("LinkedIn get_engagement failed ({}): {}", status, body_text);
        }

        let json: serde_json::Value = response
            .json()
            .await
            .context("Failed to parse get_engagement response")?;

        let likes = json
            .get("likesSummary")
            .and_then(|v| v.get("totalLikes"))
            .and_then(|v| v.as_u64())
            .unwrap_or(0);

        let comments = json
            .get("commentsSummary")
            .and_then(|v| v.get("totalFirstLevelComments"))
            .and_then(|v| v.as_u64())
            .unwrap_or(0);

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. For 403, request the analytics product/scope for the app and re-authorize.
  2. Ensure the URN matches the post type you created (share vs ugcPost vs posts API URN).
  3. For 401, refresh the token; for 429, lengthen the polling interval.
  4. Only query posts owned by the authorized member or organization.
Defensive patterns

Strategy: try-catch

Try / catch

match client.get_engagement(&token, &post_urn).await {
    Ok(stats) => record(stats),
    Err(e) if e.to_string().contains("(403") => { /* analytics not granted; degrade silently or alert once */ }
    Err(e) if e.to_string().contains("(429") => { /* back off polling */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Fetching engagement for a post the token's member does not own; missing analytics scope (403); expired token (401); deleted post (404); using share statistics endpoints on ugcPost URNs or vice versa.

Common situations: Dashboards polling metrics with tokens granted only for posting; URN kind mismatches after LinkedIn's share/ugcPost migration; rate limits on frequent polling.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/0a3122b92eb605d7. Report an issue: GitHub.