{"record":{"id":"9b19f12488516631","repo":"zeroclaw-labs/zeroclaw","slug":"linkedin-add-reaction-failed","errorCode":null,"errorMessage":"LinkedIn add_reaction failed ({}): {}","messagePattern":"LinkedIn add_reaction failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/linkedin_client.rs","lineNumber":423,"sourceCode":"\n    pub async fn add_reaction(&self, post_id: &str, reaction_type: &str) -> anyhow::Result<()> {\n        let creds = self.get_credentials().await?;\n        let actor_urn = format!(\"urn:li:person:{}\", creds.person_id);\n        let url = format!(\"{}/rest/reactions?actor={}\", LINKEDIN_API_BASE, actor_urn);\n\n        let body = json!({\n            \"reactionType\": reaction_type,\n            \"object\": post_id\n        });\n\n        let response = self\n            .api_request(Method::POST, &url, &creds.access_token, Some(body))\n            .await?;\n\n        let status = response.status();\n        if !status.is_success() {\n            let body_text = response.text().await.unwrap_or_default();\n            anyhow::bail!(\"LinkedIn add_reaction failed ({}): {}\", status, body_text);\n        }\n\n        Ok(())\n    }\n\n    pub async fn delete_post(&self, post_id: &str) -> anyhow::Result<()> {\n        let creds = self.get_credentials().await?;\n        let url = format!(\"{}/rest/posts/{}\", LINKEDIN_API_BASE, post_id);\n\n        let response = self\n            .api_request(Method::DELETE, &url, &creds.access_token, None)\n            .await?;\n\n        let status = response.status();\n        if !status.is_success() {\n            let body_text = response.text().await.unwrap_or_default();\n            anyhow::bail!(\"LinkedIn delete_post failed ({}): {}\", status, body_text);\n        }","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/linkedin_client.rs#L405-L441","documentation":"Thrown when POST of a reaction (like) to a LinkedIn post returns a non-2xx status, body included. Reactions need the social scope and a valid target URN; the endpoint returns no JSON payload on success, so this status check is the only failure signal. Same failure families as comments.","triggerScenarios":"Reacting to a deleted or malformed post URN (404/400); token missing w_member_social (403); expired token (401); rate limiting (429).","commonSituations":"Automation liking posts discovered via list_posts after the author deleted them; scope drift after app reconfiguration; tokens older than the 60-day lifetime.","solutions":["For 404/400, re-fetch the post list and use a fresh URN.","For 403, grant w_member_social and re-authorize the member.","For 401, ensure a refresh token is stored so the client can renew, or re-run the OAuth flow.","Rate-limit (429) reactions by backing off per LinkedIn's Retry-After guidance."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.add_reaction(&token, &target_urn, reaction).await {\n    Ok(()) => Ok(()),\n    Err(e) if e.to_string().contains(\"(404\") => Ok(()), // already gone: nothing to like\n    Err(e) => Err(e),\n}","preventionTips":["Treat reactions as idempotent: a 404 target means the work is done.","Verify the URN came from the same member's feed before reacting.","Keep the social scope on the token; reaction endpoints enforce it like posts."],"tags":["linkedin","http-status","reactions"],"backgroundTag":"http-error-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}