{"record":{"id":"e491f900ba043451","repo":"astrid-runtime/astrid","slug":"unexpected-response-shape-other-e491f9","errorCode":null,"errorMessage":"unexpected response shape: {other:?}","messagePattern":"unexpected response shape: (.+?)","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/auth.rs","lineNumber":142,"sourceCode":"    // `InviteRedeem` doesn't need a verified caller principal — the\n    // token is the auth and the kernel's admin dispatcher bypasses\n    // the cap-gate for this variant. Stamp the IPC message with the\n    // `default` principal so the kernel's `resolve_caller` has *a*\n    // value to log; the handler ignores it.\n    let client = state.admin_client(PrincipalId::default())?;\n    let resp = client\n        .request(AdminRequestKind::InviteRedeem {\n            token: body.token,\n            public_key: body.public_key,\n            display_name: body.display_name,\n        })\n        .await\n        .map_err(|e| GatewayError::Internal(anyhow::anyhow!(\"daemon request: {e}\")))?;\n    let redeemed = match resp {\n        AdminResponseBody::InviteRedeemed(r) => r,\n        AdminResponseBody::Error(msg) => return Err(GatewayError::Kernel(msg)),\n        other => {\n            return Err(GatewayError::Internal(anyhow::anyhow!(\n                \"unexpected response shape: {other:?}\"\n            )));\n        },\n    };\n\n    let session_token = mint_bearer(\n        &state.signing.signer,\n        &redeemed.principal,\n        state.config.session_lifetime_secs,\n    );\n    let session_expires_at_epoch = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .map_or(0, |d| d.as_secs())\n        .saturating_add(state.config.session_lifetime_secs);\n\n    Ok(Json(RedeemResponse {\n        principal: redeemed.principal,\n        group: redeemed.group,","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/auth.rs#L124-L160","documentation":"post_redeem treats an AdminResponseBody that is neither InviteRedeemed nor Error as a protocol violation and raises Internal with \"unexpected response shape: {other:?}\". It indicates the daemon replied successfully but with a variant the gateway didn't expect for an InviteRedeem request — usually a version skew between gateway and kernel.","triggerScenarios":"POST /api/auth/redeem where the daemon's response deserializes to an AdminResponseBody variant other than InviteRedeemed or Error (e.g. PairToken, or a newer/older variant).","commonSituations":"Gateway and kernel built from mismatched versions so request/response enums drifted; a proxy or buggy daemon returning a wrong response type for the request.","solutions":["Ensure gateway and kernel/daemon are built from compatible versions and redeploy together.","Inspect the {other:?} payload to identify which variant came back and why.","Add/extend a matching AdminResponseBody variant if the protocol intentionally gained a new response.","Report/fix the daemon handler that answered InviteRedeem with the wrong response type."],"exampleFix":"// before: version-skewed pair\ngateway v1.2 (expects InviteRedeemed) <-> kernel v1.3 (returns InviteRedeemedV2)\n// after: matching versions\n$ cargo build --workspace && redeploy gateway + kernel together","handlingStrategy":"try-catch","validationCode":"// assert protocol compatibility at deploy time\ngatewayVersion === kernelVersion || throw new Error('gateway/kernel version skew');","typeGuard":"fn is_redeem_response(resp: &AdminResponseBody) -> bool {\n    matches!(resp, AdminResponseBody::InviteRedeemed(_) | AdminResponseBody::Error(_))\n}","tryCatchPattern":"match resp {\n    AdminResponseBody::InviteRedeemed(r) => Ok(r),\n    AdminResponseBody::Error(msg) => Err(GatewayError::Kernel(msg)),\n    other => Err(GatewayError::Internal(anyhow!(\"unexpected response shape: {other:?}\"))),\n} // on this error: pin gateway+kernel to matching versions and redeploy","preventionTips":["Deploy gateway and kernel from the same workspace build/commit.","Add a protocol-version handshake between gateway and daemon.","Log the full {other:?} payload to speed up version-skew diagnosis.","Add round-trip tests for each AdminRequestKind/Response pair."],"tags":["rust","protocol","version-skew","daemon"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}