astrid-runtime/astrid · error · GatewayError::Internal

no unique compatible loaded capsule handles the registry…

Error message

no unique compatible loaded capsule handles the registry request for caller

What it means

`provider_source_ids` throws this GatewayError::Internal when the topic probe IS subscribed for the scoped key, but `subscriber_source_ids` returns an empty list. The subscription exists yet no unique compatible loaded capsule is registered as a source for it — the gateway found the topic but no provider instance that can answer with a usable source id.

Solutions

  1. Inspect which capsules are loaded and confirm the registry provider capsule registered its source id after subscribing; restart/reload the capsule so registration completes.
  2. Check for a stale probe subscription from an unloaded capsule; clear/reset the probe subscription state and retry.
  3. Look for races between subscription and source registration in the capsule load path and make registration atomic with subscription.
  4. Check kernel/capsule logs for errors during provider registration that left the subscription present but the source list empty.
Defensive patterns

Strategy: retry

Validate before calling

let ids = probe.subscriber_source_ids(&key).await;
if ids.is_empty() {
    return Err(anyhow!("no capsule source registered for {key}; aborting registry request"));
}

Type guard

fn has_provider_source(ids: &[Uuid]) -> bool {
    !ids.is_empty()
}

Try / catch

match registry_round_trip(&state, /* ... */).await {
    Err(e) if e.to_string().contains("no unique compatible loaded capsule") => {
        // transient: subscription present but source registration lagging — retry once, then 503
        Err(StatusCode::SERVICE_UNAVAILABLE)
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `registry_round_trip` where the probe is subscribed to `scoped_topic_probe_key(principal, request_topic)` but `probe.subscriber_source_ids(&key).await` yields an empty Vec.

Common situations: A capsule subscribed to the probe topic but never registered its provider source id; a stale subscription left after the capsule was unloaded or crashed; a race where the probe saw the subscription before the capsule completed registration; an inconsistent registry state with zero registered provider instances.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-gateway/src/routes/models.rs:347

async fn provider_source_ids(
    state: &GatewayState,
    principal: &PrincipalId,
    request_topic: &str,
) -> GatewayResult<Vec<Uuid>> {
    let probe = state.topic_probe.as_ref().ok_or_else(|| {
        GatewayError::Internal(anyhow::anyhow!(
            "gateway has no live capsule provider probe"
        ))
    })?;
    let key = scoped_topic_probe_key(principal, request_topic);
    if !probe.is_subscribed(&key).await && !probe.ensure_subscribed(&key).await {
        return Err(GatewayError::Internal(anyhow::anyhow!(
            "no loaded capsule handles the registry request for caller"
        )));
    }
    let source_ids = probe.subscriber_source_ids(&key).await;
    if source_ids.is_empty() {
        return Err(GatewayError::Internal(anyhow::anyhow!(
            "no unique compatible loaded capsule handles the registry request for caller"
        )));
    }
    Ok(source_ids)
}

fn scoped_topic_probe_key(principal: &PrincipalId, topic: &str) -> String {
    format!(
        "{SCOPED_SERVICE_PROBE_SENTINEL}{principal}\0astrid\0registry\0{REGISTRY_INTERFACE_REQUIREMENT}\0{topic}"
    )
}

/// `GET /api/models` — list the caller's available provider-entries.
#[utoipa::path(
    get,
    path = "/api/models",
    tag = "models",
    responses(

View on GitHub (pinned to affd8760f4)