astrid-runtime/astrid · error · GatewayError::Internal
registry reply was not an IPC message
Error message
registry reply was not an IPC message
What it means
After receiving an event on the reply channel, `registry_round_trip` destructures it expecting `AstridEvent::Ipc`. Any other event variant triggers `GatewayError::Internal('registry reply was not an IPC message')`. This guards the invariant that only IPC messages should arrive on the registry reply subscription.
Source
Thrown at crates/astrid-gateway/src/routes/models.rs:279
.unwrap_or_else(tokio::time::Instant::now);
loop {
let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
// No budget left (deadline already elapsed, or zero remaining): break
// to the timeout path rather than calling `recv(Some(ZERO))`, whose
// behaviour with a zero duration is implementation-defined. The
// absolute deadline above keeps total wait bounded regardless.
if remaining.is_zero() {
return Err(GatewayError::Internal(anyhow::anyhow!(
"registry did not respond"
)));
}
let Some(event) = reply_rx.recv(Some(remaining)).await else {
return Err(GatewayError::Internal(anyhow::anyhow!(
"registry did not respond"
)));
};
let AstridEvent::Ipc { message, .. } = &*event else {
return Err(GatewayError::Internal(anyhow::anyhow!(
"registry reply was not an IPC message"
)));
};
if !expected_source_ids.contains(&message.source_id) {
continue;
}
// Extract the guest-facing payload. Capsule `publish_json` arrives as
// `Custom { data }` when the JSON has no known IPC `type`; using the
// guest bytes unwraps that data instead of exposing the internal tagged
// wrapper to the HTTP API.
let value = registry_reply_payload_json(&message.payload)?;
// Skip a reply that belongs to a different concurrent same-principal
// SET (foreign `corr_id`); keep waiting within the remaining budget.
if reply_satisfies_corr_id(&value, corr_id) {
return Ok(value);
}
}
}View on GitHub (pinned to affd8760f4)
Solutions
- Log the received event variant to identify what is being published to the reply topic.
- Narrow the reply subscription to the exact response topic/correlation key so unrelated events cannot arrive.
- Align event enum versions between gateway and bus publishers.
- If other variants are legitimately possible, handle/skip them instead of failing the round trip.
Example fix
// before
let AstridEvent::Ipc { message, .. } = &*event else {
return Err(GatewayError::Internal(anyhow::anyhow!(
"registry reply was not an IPC message"
)));
};
// after
let AstridEvent::Ipc { message, .. } = &*event else {
tracing::debug!(event_kind = event.kind(), "ignoring non-IPC event on reply topic");
continue;
}; Defensive patterns
Strategy: type-guard
Type guard
fn as_ipc_event(event: &AstridEvent) -> Option<&IpcMessage> {
match event {
AstridEvent::Ipc { message, .. } => Some(message),
_ => None,
}
} Try / catch
match registry_round_trip(state, principal, req_topic, resp_topic, payload, corr).await {
Err(e) if e.message().contains("was not an IPC message") => {
tracing::warn!("non-IPC event on reply topic; treating as transient");
registry_round_trip(state, principal, req_topic, resp_topic, payload, corr).await
}
r => r,
} Prevention
- Subscribe reply channels to exact topics, never wildcards.
- Skip-and-continue on non-IPC events instead of aborting the round trip when subscriptions may be broad.
- Keep AstridEvent enum versions aligned across publisher and gateway.
- Add a debug log of unexpected event kinds for observability.
When it happens
Trigger: The event bus delivers a non-IPC `AstridEvent` (e.g. a lifecycle, log, or control event) to the registry reply subscription — possible from over-broad topic subscriptions or a misbehaving bus publisher.
Common situations: Wildcard/overlapping topic subscriptions routing unrelated events to the reply channel; a bus implementation broadcasting control events to all subscribers; a test harness publishing raw events; version mismatch where event enums diverged between publisher and gateway.
Related errors
- registry reply not JSON: {e}
- gateway is not wired to a live event bus; model registry una
- registry did not respond
- MCP gateway rejected {operation:?}: {}
- gateway is not wired to a live event bus; agent approval una
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/fe68146d9b9d5b8d.
Report an issue: GitHub.