{"record":{"id":"681bece66af5de41","repo":"jlcodes99/cockpit-tools","slug":"invalid-bundle-root","errorCode":"invalid_bundle_root","errorMessage":"invalid_bundle_root","messagePattern":"invalid_bundle_root","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/accountTransferService.ts","lineNumber":331,"sourceCode":"    version: ACCOUNT_TRANSFER_VERSION,\n    exported_at: new Date().toISOString(),\n    summary: {\n      platform_count: ALL_PLATFORM_IDS.length,\n      account_count: accountCount,\n    },\n    platforms,\n  };\n}\n\nexport async function exportAllAccountsTransferJson(): Promise<string> {\n  const bundle = await buildAccountTransferBundle();\n  return JSON.stringify(bundle, null, 2);\n}\n\nfunction parseAccountTransferBundle(jsonContent: string): Record<PlatformId, AccountTransferPlatformPayload> {\n  const parsed = parseJsonOrThrow(jsonContent, 'invalid_json');\n  if (!isRecord(parsed)) {\n    throw new Error('invalid_bundle_root');\n  }\n\n  if (parsed.schema !== ACCOUNT_TRANSFER_SCHEMA) {\n    throw new Error('invalid_bundle_schema');\n  }\n\n  if (parsed.version !== ACCOUNT_TRANSFER_VERSION) {\n    throw new Error('invalid_bundle_version');\n  }\n\n  const rawPlatforms = parsed.platforms;\n  if (!isRecord(rawPlatforms)) {\n    throw new Error('invalid_bundle_platforms');\n  }\n\n  const platforms: Record<PlatformId, AccountTransferPlatformPayload> = {} as Record<\n    PlatformId,\n    AccountTransferPlatformPayload","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/services/accountTransferService.ts#L313-L349","documentation":"parseAccountTransferBundle rejects a transfer bundle whose parsed root is not a JSON object (a record). The importer expects Record<PlatformId, AccountTransferPlatformPayload>-shaped input, so arrays, strings, numbers, or null at the root trigger this code.","triggerScenarios":"The JSON string parses successfully (via parseJsonOrThrow) but isRecord(parsed) is false — e.g. the file contains a top-level array '[...]', a bare string, number, boolean, or null.","commonSituations":"Hand-editing a bundle into an array of platforms, exporting only an inner section instead of the full bundle, pasting a fragment of a bundle.","solutions":["Ensure the bundle's top level is a JSON object containing schema, version, and platforms keys","Re-export the bundle with exportAccountTransferBundle so the wrapper object is produced","Wrap arrays in an object or remove the extra outer array layer","Check the file was not truncated after an opening brace or exported from a different schema"],"exampleFix":"// before\n[ { \"openai\": { ... } } ]\n// after\n{ \"schema\": \"...\", \"version\": 1, \"platforms\": { \"openai\": { ... } } }","handlingStrategy":"validation","validationCode":"const obj = JSON.parse(text);\nif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n  throw new Error('invalid_bundle_root');\n}","typeGuard":"function isBundleRoot(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) && 'platforms' in v;\n}","tryCatchPattern":"try {\n  const bundle = importAccountTransferBundle(text);\n} catch (e) {\n  if (e instanceof Error && e.message === 'invalid_bundle_root') {\n    alert('Import file must be a transfer bundle object, not an array or scalar.');\n  } else throw e;\n}","preventionTips":["Always export via the app's export function so the root object shape is guaranteed","Check the first non-whitespace character of the file: '{' for valid bundles","Do not wrap bundle contents in arrays when editing manually","Validate the bundle shape before calling import"],"tags":["validation","json","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}