astrid-runtime/astrid · error

request arrives

Error message

request arrives

What it means

This panic comes from `req_rx.recv().await.expect("request arrives")` inside the stand-in capsule task in `request_capsule_round_trips_update_reply`. It fires when the subscribed EventBus topic stream ends (recv returns None) before any `update` request event is delivered, i.e. the subscription closed without a message.

Source

Thrown at crates/astrid-gateway/src/routes/sessions_tests.rs:729

    ));
}

/// Live round-trip over a real `EventBus` for the `update` verb: the
/// stand-in capsule receives the principal-stamped, present-keys-only
/// patch and replies with an updated SUMMARY on the scoped topic.
#[tokio::test]
async fn request_capsule_round_trips_update_reply() {
    let bus = Arc::new(EventBus::new());
    let principal = PrincipalId::new("alice").expect("valid principal");
    let correlation_id = "corr-upd-1";
    let response_topic = format!("{TOPIC_UPDATE_RESPONSE_PREFIX}.{correlation_id}");

    let mut req_rx = bus.subscribe_topic(TOPIC_UPDATE_REQUEST.to_string());
    let bus_capsule = Arc::clone(&bus);
    let resp_topic = response_topic.clone();
    let cid = correlation_id.to_string();
    let capsule = tokio::spawn(async move {
        let event = req_rx.recv().await.expect("request arrives");
        let AstridEvent::Ipc { message, .. } = &*event else {
            panic!("expected IPC request");
        };
        // Principal-stamped, and the patch carries only the sent key.
        assert_eq!(message.principal.as_deref(), Some("alice"));
        assert_ne!(message.source_id, Uuid::nil());
        assert_eq!(message.origin, MessageOrigin::RemoteGateway);
        if let IpcPayload::RawJson(v) = &message.payload {
            assert_eq!(v["title"], "renamed");
            assert!(v.get("archived").is_none(), "absent key not forwarded");
        } else {
            panic!("expected RawJson payload");
        }
        let reply = serde_json::json!({
            "correlation_id": cid,
            "session": {
                "session_id": "sess-1",
                "title": "renamed",

View on GitHub (pinned to affd8760f4)

Solutions

  1. Confirm the helper publishes to exactly `TOPIC_UPDATE_REQUEST` for the update verb.
  2. Check whether the update helper returned Err and surface that error first; a helper failure usually starves this receiver.
  3. Keep the publisher (bus Arc) alive for the duration of the test so the channel cannot close early.
  4. Replace expect with explicit matching on None vs Some to distinguish channel-closed from timeout.

Example fix

// before
let event = req_rx.recv().await.expect("request arrives");
// after
let Some(event) = req_rx.recv().await else {
    panic!("update request channel closed before a request arrived");
};
Defensive patterns

Strategy: try-catch

Try / catch

match req_rx.recv().await {
    Some(event) => { /* handle */ }
    None => panic!("request channel closed before delivery"),
}

Prevention

When it happens

Trigger: The helper under test (`update_session` path) never publishes an `AstridEvent::Ipc` on `TOPIC_UPDATE_REQUEST` before the capsule task's receiver channel closes — e.g. the helper errored out early, published on a different topic name, or the EventBus dropped its publisher side.

Common situations: Topic constant renamed so producer and consumer disagree; the helper returns Err before publishing (surface it via the other expect at line ~780); async task raced and the bus was dropped; EventBus subscription API changed semantics so recv now ends on idle.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/2ca44301575808b1. Report an issue: GitHub.