{"record":{"id":"b3f01bcdeda100cd","repo":"xai-org/grok-build","slug":"produced-json-that-is-not-a-token-payload-e","errorCode":null,"errorMessage":"produced JSON that is not a token payload: {e}","messagePattern":"produced JSON that is not a token payload: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/auth/token_output.rs","lineNumber":66,"sourceCode":"    output: &std::process::Output,\n) -> anyhow::Result<ParsedTokenOutput> {\n    if !output.status.success() {\n        anyhow::bail!(\"exited with {}\", output.status);\n    }\n    let stdout = std::str::from_utf8(&output.stdout)\n        .map_err(|_| anyhow::anyhow!(\"produced non-UTF-8 output on stdout\"))?\n        .trim();\n    if stdout.is_empty() {\n        anyhow::bail!(\"produced no output on stdout\");\n    }\n\n    // Output that starts with `{` is meant to be a token payload: require it to\n    // parse and carry a non-empty access_token. Anything else is a bare token\n    // (JWTs and opaque tokens never start with `{`), so an error object like\n    // `{\"error\":\"expired\"}` can never be mistaken for a bearer.\n    if stdout.starts_with('{') {\n        let parsed: ExternalAuthOutput = serde_json::from_str(stdout)\n            .map_err(|e| anyhow::anyhow!(\"produced JSON that is not a token payload: {e}\"))?;\n        let access_token = parsed.access_token.trim().to_owned();\n        if access_token.is_empty() {\n            anyhow::bail!(\"produced JSON with an empty access_token\");\n        }\n        reject_control_chars(&access_token)?;\n        tracing::debug!(\n            has_refresh_token = parsed.refresh_token.is_some(),\n            expires_in = ?parsed.expires_in,\n            issuer = ?parsed.issuer,\n            \"auth: parsed external provider output as JSON\"\n        );\n        return Ok(ParsedTokenOutput {\n            access_token,\n            refresh_token: parsed.refresh_token,\n            expires_at: parsed.expires_in.and_then(expiry_after_seconds),\n            issuer: parsed\n                .issuer\n                .map(|i| i.trim().to_owned())","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/auth/token_output.rs#L48-L84","documentation":"When the provider's stdout starts with '{', parse_token_output treats it strictly as a token payload and deserializes it into ExternalAuthOutput, requiring an access_token. If serde_json cannot deserialize the text into that struct, this error is returned (wrapped by the caller with the command name). This prevents error objects like {\"error\":\"expired\"} from being mistaken for bearer tokens.","triggerScenarios":"The provider prints JSON on stdout that doesn't match ExternalAuthOutput's schema: missing access_token, wrong field types (e.g. numeric expires_in where string expected), extra incompatible shapes, or nested error objects.","commonSituations":"Provider returns {\"error\":...} on failure; provider upgraded and changed JSON field names; snake_case vs camelCase mismatch; provider emits an envelope like {\"data\":{\"access_token\":...}} instead of a flat object.","solutions":["Print the raw JSON the provider produced and compare it against the published ExternalAuthOutput contract (flat object with non-empty access_token).","Fix the provider to emit the exact expected schema (snake_case fields, access_token present and non-empty).","On provider-side errors, exit non-zero or print non-JSON diagnostics to stderr so the shell reports the real failure.","Update the shell or provider so both sides agree on the contract version."],"exampleFix":"// before\n{\"result\":{\"access_token\":\"eyJ...\"}}\n// after\n{\"access_token\":\"eyJ...\",\"refresh_token\":\"...\"}","handlingStrategy":"type-guard","validationCode":"const out = execSync(provider_cmd, { encoding: 'utf8' }).trim();\nif (out.startsWith('{')) {\n  const j = JSON.parse(out);\n  if (!('access_token' in j) || !String(j.access_token).trim()) {\n    throw new Error('provider JSON missing non-empty access_token');\n  }\n}","typeGuard":"function isExternalAuthOutput(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) &&\n    typeof v.access_token === 'string' && v.access_token.trim().length > 0;\n}","tryCatchPattern":"match run_auth_flow(...).await {\n    Err(e) if e.to_string().contains(\"produced JSON that is not a token payload\") => {\n        eprintln!(\"Provider returned malformed/error JSON — diff its stdout against the ExternalAuthOutput contract\");\n    }\n    other => other?,\n}","preventionTips":["Validate provider JSON against the contract in the provider's own tests","Use a flat snake_case schema with a non-empty access_token","Report provider errors via exit code + stderr, never as stdout JSON","Bump and test both sides together when the schema changes"],"tags":["auth","json","schema-validation","external-provider"],"backgroundTag":"invalid-token-payload","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}