zeroclaw-labs/zeroclaw · error · anyhow::Error
Notion search failed ({status}): {truncated}
Error message
Notion search failed ({status}): {truncated} What it means
The Notion tool's search action sent POST https://api.notion.com/v1/search and received a non-2xx status; the message carries the status and a truncated error body. Because search only touches content already shared with the integration, failures here are usually authentication, rate limiting, or malformed query payloads rather than per-object access.
Source
Thrown at crates/zeroclaw-tools/src/notion_tool.rs:174
/// Search the Notion workspace by query string.
async fn search(&self, query: &str) -> anyhow::Result<serde_json::Value> {
let url = format!("{NOTION_API_BASE}/search");
let body = json!({ "query": query });
let resp = self
.http
.post(&url)
.headers(self.headers()?)
.json(&body)
.timeout(std::time::Duration::from_secs(NOTION_REQUEST_TIMEOUT_SECS))
.send()
.await?;
let status = resp.status();
if !status.is_success() {
let text = resp.text().await.unwrap_or_default();
let truncated =
crate::util_helpers::truncate_with_ellipsis(&text, MAX_ERROR_BODY_CHARS);
anyhow::bail!("Notion search failed ({status}): {truncated}");
}
resp.json().await.map_err(Into::into)
}
}
#[async_trait]
impl Tool for NotionTool {
fn name(&self) -> &str {
"notion"
}
fn description(&self) -> &str {
"Interact with Notion: query databases, read/create/update pages, and search the workspace."
}
fn parameters_schema(&self) -> serde_json::Value {
json!({
"type": "object",View on GitHub (pinned to 88bb9c8533)
Solutions
- For 429, throttle and retry with backoff; cache search results instead of searching per operation
- For 401/403, regenerate the integration secret and re-share the workspace content
- For 400, simplify the payload: drop filter/sort and send only the query string
- Remember search only sees pages/databases explicitly shared with the integration; empty results with 200 are a sharing issue, not this error
Example fix
// before (tight loop)
for page in candidates { tool.execute({"action":"search","query":page}) }
// after (search once, then act)
let found = tool.execute({"action":"search","query":"meeting notes"}).await?;
for page in found.results { tool.execute({"action":"read_page","page_id":page.id}).await? } Defensive patterns
Strategy: retry
Validate before calling
// Throttle searches yourself: token bucket at ~2/s leaves headroom
let mut bucket = governor_default();
for q in queries { bucket.wait().await; notion.execute(json!({"action":"search","query":q})).await?; } Try / catch
Err(e) if e.to_string().starts_with("Notion search failed (429)") => {
tokio::time::sleep(Duration::from_secs(2u64.pow(attempt).min(32))).await; // retry
} Prevention
- Cache search results for the session instead of searching per operation
- Batch discovery once at startup rather than continuously
- Monitor for 401 flips after token rotation so the first failure alerts, not the hundredth
When it happens
Trigger: Invalid or revoked integration token (401), token without workspace access (403), a filter/sort payload with an invalid shape or unknown property (400), or hammering the endpoint past ~3 requests/second (429 rate_limited).
Common situations: Agents that call search before every page operation quickly trip 429. Token rotation without updating config produces sudden 401s on the very first call of a session.
Related errors
- Notion query_database failed ({status}): {truncated}
- Notion read_page failed ({status}): {truncated}
- Notion create_page failed ({status}): {truncated}
- Notion update_page failed ({status}): {truncated}
- Embedding API error {status}: {text}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/99127d1c5724397f.
Report an issue: GitHub.