zeroclaw-labs/zeroclaw · error

LinkedIn delete_post failed ({}): {}

Error message

LinkedIn delete_post failed ({}): {}

What it means

Thrown when DELETE of a LinkedIn post returns a non-2xx status, body included. Deletion requires the token owner to be the author of the post (or an organization admin for company posts); otherwise LinkedIn answers 403. A successful delete returns 204, so this guard is the sole failure path.

Source

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

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

        Ok(())
    }

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

        let response = self
            .api_request(Method::DELETE, &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 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);
        }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Confirm the post was created by the same member whose token is in use.
  2. Treat 404 as success in idempotent cleanup code (post already gone).
  3. For 403 on company posts, use a token from an organization admin.
  4. For 401, refresh the token.

Example fix

// before
client.delete_post(&post_urn).await?; // aborts on already-deleted 404

// after: treat missing post as done
match client.delete_post(&post_urn).await {
    Ok(()) => {}
    Err(e) if e.to_string().contains("404") => { /* already deleted */ }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Try / catch

// Idempotent delete: 404 is success
match client.delete_post(&token, &post_urn).await {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("(404") => Ok(()),
    Err(e) if e.to_string().contains("(403") => Err(e).context("not the post author; use author's token"),
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Deleting a post URN that no longer exists (404); deleting a post authored by a different member with this member's token (403); token lacking management scope; expired token (401).

Common situations: Cleanup jobs deleting posts from a list captured days earlier; switching the authorized member between creation and deletion; deleting organization posts without admin rights.

Related errors


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