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

daemon request: {e}

Error message

daemon request: {e}

What it means

post_redeem (invite redemption) maps any failure of the admin/daemon client request into an Internal (500) error "daemon request: {e}". It means the gateway could not get a successful response from the kernel/daemon for the InviteRedeem admin request — transport, connection, or client-level failure.

Source

Thrown at crates/astrid-gateway/src/routes/auth.rs:137

                    .max(1),
            });
        }
    }

    // `InviteRedeem` doesn't need a verified caller principal — the
    // token is the auth and the kernel's admin dispatcher bypasses
    // the cap-gate for this variant. Stamp the IPC message with the
    // `default` principal so the kernel's `resolve_caller` has *a*
    // value to log; the handler ignores it.
    let client = state.admin_client(PrincipalId::default())?;
    let resp = client
        .request(AdminRequestKind::InviteRedeem {
            token: body.token,
            public_key: body.public_key,
            display_name: body.display_name,
        })
        .await
        .map_err(|e| GatewayError::Internal(anyhow::anyhow!("daemon request: {e}")))?;
    let redeemed = match resp {
        AdminResponseBody::InviteRedeemed(r) => r,
        AdminResponseBody::Error(msg) => return Err(GatewayError::Kernel(msg)),
        other => {
            return Err(GatewayError::Internal(anyhow::anyhow!(
                "unexpected response shape: {other:?}"
            )));
        },
    };

    let session_token = mint_bearer(
        &state.signing.signer,
        &redeemed.principal,
        state.config.session_lifetime_secs,
    );
    let session_expires_at_epoch = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map_or(0, |d| d.as_secs())

View on GitHub (pinned to affd8760f4)

Solutions

  1. Check the daemon is running and reachable (socket/address in gateway config).
  2. Look at the wrapped {e} message for the exact transport failure (connection refused vs timeout).
  3. Redeploy/restart the kernel daemon if it crashed, then retry redemption.
  4. Verify gateway-to-daemon configuration matches the daemon's actual listen address.

Example fix

// before: config pointing at a dead socket
admin_addr = "/run/astrid/daemon.sock"
// after: ensure daemon is up and address correct
$ systemctl status astrid-daemon  # then restart gateway
Defensive patterns

Strategy: retry

Validate before calling

// precheck daemon reachability before redemption
const ok = await fetch('/api/health'); // gateway health implies daemon link
if (!ok.ok) throw new Error('daemon unreachable; retry later');

Try / catch

match client.request(AdminRequestKind::InviteRedeem { .. }).await {
    Ok(resp) => resp,
    Err(e) if e.is_connect() || e.is_timeout() => backoff_retry(|| client.request(req.clone())).await
        .map_err(|e| GatewayError::Internal(anyhow!("daemon request: {e}")))?,
    Err(e) => return Err(GatewayError::Internal(anyhow!("daemon request: {e}"))),
}

Prevention

When it happens

Trigger: POST /api/auth/redeem where client.request(AdminRequestKind::InviteRedeem{...}).await returns Err — daemon unreachable, connection refused, request timeout, or serialization failure.

Common situations: Kernel daemon not running or crashed; wrong daemon socket/URL configured; network partition between gateway and daemon; daemon overloaded and timing out.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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