{"record":{"id":"656e9800982bcfc5","repo":"Wei-Shaw/sub2api","slug":"chatgpt-devicecheck-returned-a-malformed-attestati","errorCode":null,"errorMessage":"ChatGPT DeviceCheck returned a malformed attestation","messagePattern":"ChatGPT DeviceCheck returned a malformed attestation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/internal/platform/liveattestation/attestation_darwin.go","lineNumber":136,"sourceCode":"\tvar stderr bytes.Buffer\n\tcommand.Stdout = &stdout\n\tcommand.Stderr = &stderr\n\tif err := command.Run(); err != nil {\n\t\tif errors.Is(runCtx.Err(), context.DeadlineExceeded) {\n\t\t\treturn \"\", errors.New(\"ChatGPT DeviceCheck token generation timed out\")\n\t\t}\n\t\treason := strings.TrimSpace(stderr.String())\n\t\tif len(reason) > 240 {\n\t\t\treason = reason[:240]\n\t\t}\n\t\tif reason == \"\" {\n\t\t\treason = err.Error()\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"ChatGPT DeviceCheck token generation failed: %s\", reason)\n\t}\n\theader := strings.TrimSpace(stdout.String())\n\tif len(header) < 20 || len(header) > 16*1024 || !json.Valid([]byte(header)) {\n\t\treturn \"\", errors.New(\"ChatGPT DeviceCheck returned a malformed attestation\")\n\t}\n\treturn header, nil\n}\n\nfunc (p *darwinProvider) findApplication() (string, error) {\n\tfor _, appPath := range p.appPaths {\n\t\tinfo, err := os.Stat(appPath)\n\t\tif err == nil && info.IsDir() {\n\t\t\treturn appPath, nil\n\t\t}\n\t}\n\treturn \"\", ErrChatGPTAppMissing\n}\n\nfunc readBundleIdentifier(ctx context.Context, appPath string) (string, error) {\n\tinfoPlist := filepath.Join(appPath, \"Contents\", \"Info.plist\")\n\toutput, err := exec.CommandContext(\n\t\tctx,","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/backend/internal/platform/liveattestation/attestation_darwin.go#L118-L154","documentation":"After the DeviceCheck subprocess succeeds, its stdout must be a JSON token header between 20 bytes and 16 KiB. If stdout is too short, too long, empty, or not valid JSON, the attestation is deemed malformed and rejected. This guards the downstream Live protocol from forwarding garbage tokens.","triggerScenarios":"The bundled node process exits 0 but prints a non-JSON banner, an error message, extra logging, or nothing; stdout pollution from the runtime; or a truncated buffer due to the process being killed mid-write (but with a non-deadline error).","commonSituations":"ChatGPT app updated and its devicecheck.node contract changed (extra output, different format); environment variables leaking extra stdout; locale-specific messages printed by node; module returning an HTML error string instead of JSON.","solutions":["Update (or pin) the official ChatGPT app to a version known compatible with Sub2API's attestation shim.","Run the subprocess manually with the same env (SUB2API_DEVICECHECK_MODULE, SUB2API_ATTESTATION_BUNDLE_ID, SUB2API_ATTESTATION_SIGNALS) and inspect stdout.","Ensure nothing (wrapper scripts, node --print banners, NVM shims) appends to stdout.","Report to Sub2API if a ChatGPT app update changed the output shape — the parser needs updating."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil {\n    if strings.Contains(err.Error(), \"malformed attestation\") {\n        // almost always a ChatGPT app version change: check app version vs known-good list\n        return appVersionError(\"update or pin the ChatGPT app bundle\")\n    }\n    return err\n}","preventionTips":["Pin/verify the ChatGPT app version on attestation hosts","Run the subprocess manually when triaging (documented SUB2API_* env vars)","Alert on this error to catch app-update breakage early"],"tags":["macos","attestation","json","subprocess","live"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}