astrid-runtime/astrid · error
empty page deserializes
Error message
empty page deserializes
What it means
This panic comes from `.expect("empty page deserializes")` in the test `parse_search_response_null_next_cursor_is_last_page`. The library helper `parse_search_response` returns an Option and yielded None for a JSON body of `{"results": [], "next_cursor": null}`, meaning the page failed to deserialize into the search-response shape. The test exists precisely to guarantee that an empty last page with a null cursor is accepted.
Source
Thrown at crates/astrid-gateway/src/routes/sessions_tests.rs:697
let parsed = parse_search_response(body).expect("frozen search shape deserializes");
assert_eq!(parsed.results.len(), 2);
assert_eq!(parsed.next_cursor.as_deref(), Some("page-2"));
let first = &parsed.results[0];
assert_eq!(first.session_id, "s1");
assert_eq!(first.title.as_deref(), Some("Trip planning"));
assert_eq!(first.match_count, 2);
assert_eq!(first.updated_at, Some(1_719_000_000));
let second = &parsed.results[1];
assert!(second.title.is_none());
assert!(second.snippet.is_none());
assert!(second.updated_at.is_none());
assert_eq!(second.match_count, 1);
}
#[test]
fn parse_search_response_null_next_cursor_is_last_page() {
let body = serde_json::json!({ "results": [], "next_cursor": null });
let parsed = parse_search_response(body).expect("empty page deserializes");
assert!(parsed.results.is_empty());
assert!(parsed.next_cursor.is_none());
// Absent next_cursor is also fine (defaults to None).
let body = serde_json::json!({ "results": [] });
assert!(parse_search_response(body).unwrap().next_cursor.is_none());
}
#[test]
fn parse_search_response_rejects_garbage() {
let body = serde_json::json!({ "results": "not-an-array" });
assert!(matches!(
parse_search_response(body).unwrap_err(),
GatewayError::Kernel(_)
));
}
/// Live round-trip over a real `EventBus` for the `update` verb: the
/// stand-in capsule receives the principal-stamped, present-keys-onlyView on GitHub (pinned to affd8760f4)
Solutions
- Inspect the body passed to parse_search_response and confirm it contains a `results` array (possibly empty) and an optional `next_cursor`.
- Update parse_search_response so a null `next_cursor` maps to None instead of failing deserialization.
- Add a serde-level unit test on the response struct with `#[serde(default)]`/Option fields to lock the empty-page contract.
- Regenerate or refresh any client types from the current OpenAPI/schema if the gateway contract changed.
Example fix
// before
let parsed = parse_search_response(body).expect("empty page deserializes");
// after
let Some(parsed) = parse_search_response(body) else {
eprintln!("body did not match search-response schema: {body}");
return;
}; Defensive patterns
Strategy: validation
Validate before calling
if body.get("results").and_then(|r| r.as_array()).is_none() {
return Err("search response missing results array");
} Type guard
fn is_valid_search_body(v: &serde_json::Value) -> bool {
v.get("results").map_or(false, |r| r.is_array())
} Try / catch
let Some(parsed) = parse_search_response(body) else {
eprintln!("unparseable search response: {body}");
return Err(ParseError::Schema);
}; Prevention
- Model the response with serde Option/default fields so null cursors parse
- Lock the empty-page contract with a dedicated unit test
- Validate payloads against the schema in CI before parser changes ship
When it happens
Trigger: Calling `parse_search_response` with a body whose shape does not match the expected struct — e.g. results missing, wrong JSON type for `results`, or `next_cursor` of an unexpected type — so the deserialization path returns None instead of Some(page).
Common situations: The session-search API changed its response envelope (field renamed from `results`, cursor renamed); a serializer emits `next_cursor: null` on the last page and an older parser rejects it; hand-rolled JSON in tests drifts from the real payload schema.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/a9a06863eeec45e7.
Report an issue: GitHub.