{"record":{"id":"233645ae191a12d1","repo":"zeroclaw-labs/zeroclaw","slug":"notion-read-page-failed-status-truncated","errorCode":null,"errorMessage":"Notion read_page failed ({status}): {truncated}","messagePattern":"Notion read_page failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/notion_tool.rs","lineNumber":97,"sourceCode":"        resp.json().await.map_err(Into::into)\n    }\n\n    /// Read a single Notion page by ID.\n    async fn read_page(&self, page_id: &str) -> anyhow::Result<serde_json::Value> {\n        let url = format!(\"{NOTION_API_BASE}/pages/{page_id}\");\n        let resp = self\n            .http\n            .get(&url)\n            .headers(self.headers()?)\n            .timeout(std::time::Duration::from_secs(NOTION_REQUEST_TIMEOUT_SECS))\n            .send()\n            .await?;\n        let status = resp.status();\n        if !status.is_success() {\n            let text = resp.text().await.unwrap_or_default();\n            let truncated =\n                crate::util_helpers::truncate_with_ellipsis(&text, MAX_ERROR_BODY_CHARS);\n            anyhow::bail!(\"Notion read_page failed ({status}): {truncated}\");\n        }\n        resp.json().await.map_err(Into::into)\n    }\n\n    /// Create a new Notion page, optionally within a database.\n    async fn create_page(\n        &self,\n        properties: &serde_json::Value,\n        database_id: Option<&str>,\n    ) -> anyhow::Result<serde_json::Value> {\n        let url = format!(\"{NOTION_API_BASE}/pages\");\n        let mut body = json!({ \"properties\": properties });\n        if let Some(db_id) = database_id {\n            body[\"parent\"] = json!({ \"database_id\": db_id });\n        }\n        let resp = self\n            .http\n            .post(&url)","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/notion_tool.rs#L79-L115","documentation":"The Notion tool's read_page action sent GET https://api.notion.com/v1/pages/{page_id} and got a non-2xx response. The status code and a 500-character excerpt of Notion's error body are included in the message so the underlying Notion error code is visible.","triggerScenarios":"Reading a page_id that was never shared with the integration (404 object_not_found), a deleted or archived page, an invalid/expired Bearer token (401), a token from a different workspace (403), or a malformed page id (400 invalid_request_error).","commonSituations":"Most often the page exists but the integration lacks access because only some pages in a workspace were shared. Also common: copying the page URL fragment wrong (missing a hex block of the UUID), and tokens revoked when someone reorganized integrations.","solutions":["Check the status in the message: 404 means access/sharing, 401 means token, 400 means bad id format","For 404, share the page (or its parent database) with the integration via '...' > Connections and retry","Verify page_id is the full 32-hex UUID from the page URL, in 8-4-4-4-12 groups or as one string","For 401, rotate the integration secret and update the configured api_key","Use the search action first to obtain valid page ids instead of hand-copying them"],"exampleFix":"// before\n{\"action\":\"read_page\",\"page_id\":\"My+Meeting+Notes\"}\n// after: resolve the real id via search, then read\n{\"action\":\"search\",\"query\":\"Meeting Notes\"}\n{\"action\":\"read_page\",\"page_id\":\"<uuid-from-search-result>\"}","handlingStrategy":"try-catch","validationCode":"// Resolve page ids via search first instead of trusting hand-copied ids\nlet found = notion.execute(json!({\"action\":\"search\",\"query\":title})).await?;\n// only read ids that came back in found[\"results\"]","typeGuard":"fn is_page_result(v: &serde_json::Value) -> bool {\n    v.get(\"object\").and_then(|o| o.as_str()) == Some(\"page\")\n        && v.get(\"id\").map(|i| i.is_string()) == Some(true)\n}","tryCatchPattern":"Err(e) if e.to_string().starts_with(\"Notion read_page failed (404)\") => {\n    // treat as missing/unshared: skip or re-share, never retry-loop\n}","preventionTips":["Derive page ids from search results, not from URLs pasted by humans","When a workspace reorganizes pages, re-share the new parents with the integration","Log the page id with failures so operators can check sharing in the Notion UI"],"tags":["notion","http","api","page","rust"],"backgroundTag":"notion-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}