{"record":{"id":"413b3066da6eb464","repo":"astrid-runtime/astrid","slug":"unexpected-response-shape-other","errorCode":null,"errorMessage":"unexpected response shape: {other:?}","messagePattern":"unexpected response shape: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/invite.rs","lineNumber":137,"sourceCode":"        AdminResponseBody::Invite(issued) => {\n            if args.raw {\n                println!(\"{}\", issued.token);\n            } else {\n                println!(\n                    \"{} {} (group: {}, uses: {}, metadata: {})\",\n                    Theme::success(\"issued\"),\n                    issued.token.bold(),\n                    issued.group,\n                    issued.remaining_uses,\n                    issued.metadata.as_deref().unwrap_or(\"-\"),\n                );\n                if let Some(exp) = issued.expires_at_epoch {\n                    println!(\"expires at unix epoch {exp}\");\n                }\n            }\n            Ok(ExitCode::SUCCESS)\n        },\n        other => anyhow::bail!(\"unexpected response shape: {other:?}\"),\n    }\n}\n\nasync fn run_redeem(args: RedeemArgs) -> Result<ExitCode> {\n    // Resolve the public key source: either an explicit `--public-key`\n    // hex string or a local `--keypair` reference. Exactly one is\n    // required (clap enforces mutual exclusion; this enforces presence).\n    let (public_key_hex, keypair_name) = match (args.public_key, args.keypair) {\n        (Some(hex), None) => (hex, None),\n        (None, Some(name)) => {\n            let hex = crate::commands::keypair::load_public_key_hex(&name)\n                .with_context(|| format!(\"load public key for --keypair {name:?}\"))?;\n            (hex, Some(name))\n        },\n        (None, None) => anyhow::bail!(\n            \"redeem requires either --public-key <hex> or --keypair <name>. \\\n             Generate one with `astrid keypair generate`.\"\n        ),","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/invite.rs#L119-L155","documentation":"run_issue in invite.rs matches on the response returned by the invite-issuing API and only recognizes the expected success variants (e.g. one carrying expires_at_epoch). Any other response shape — an error payload, a schema change, or a wrapper object — falls into the catch-all `other` arm and bails with a debug dump of the value. This signals that the server returned something the CLI does not understand, so it refuses to guess.","triggerScenarios":"`astrid invite issue` (run -> run_issue) receives an API response that deserialized into a variant other than the expected Issued shape — e.g. server returned an error object, a paginated envelope, or a new field-layout variant after an API version bump.","commonSituations":"Server/client version skew: the CLI talks to a newer or older invite service whose response JSON shape changed; hitting the wrong endpoint or environment (staging returning an error envelope); proxy or gateway returning an HTML/JSON error body that still deserializes; API deprecation changing the response enum.","solutions":["Upgrade the astrid CLI to the version matching the server's invite API — the response shape likely changed on the server side.","Check the server response directly (curl the invite endpoint) and compare with the shape the CLI expects; report the {other:?} dump in a bug report if it looks valid.","Verify you're pointing at the correct API endpoint/environment (prod vs staging) and API version path.","If the server is emitting an error, fix the underlying request (auth token, permissions) so the expected success variant is returned."],"exampleFix":"// before\n$ astrid --server https://staging.api.example.com invite issue --org acme\nunexpected response shape: Error { code: 403, message: \"forbidden\" }\n// after (correct environment/updated CLI)\n$ astrid --server https://api.example.com invite issue --org acme\nissued invite, expires at unix epoch 1760000000","handlingStrategy":"type-guard","validationCode":"// validate the expected shape before printing\nif !matches!(resp, InviteResponse::Issued(_)) {\n    eprintln!(\"invite API returned a non-issued response; check CLI/server versions\");\n}","typeGuard":"fn is_issued(resp: &InviteResponse) -> bool {\n    matches!(resp, InviteResponse::Issued(_))\n}","tryCatchPattern":"match run_issue(args).await {\n    Ok(code) => code,\n    Err(e) if e.to_string().starts_with(\"unexpected response shape\") => {\n        eprintln!(\"Server response not understood by this CLI version; upgrade astrid-cli or verify the endpoint, then retry.\");\n        ExitCode::FAILURE\n    }\n    Err(e) => { eprintln!(\"{e:#}\"); ExitCode::FAILURE }\n}","preventionTips":["Keep the CLI version in lockstep with the invite service API version","Pin the API endpoint/environment explicitly rather than relying on defaults","Log the full {other:?} payload when it occurs and file it with a bug report","Add a contract test against the live API shape when upgrading either side"],"tags":["api","response-parsing","cli","version-skew","rust"],"backgroundTag":"unexpected-response-shape","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}