chatboxai/chatbox · error

Unsupported legacy backup data format

Error message

Unsupported legacy backup data format

What it means

Thrown by importLegacyJsonBackup after it parses the selected file with JSON.parse. The legacy importer requires the top-level JSON value to be a non-null, non-array object (a key/value record, like the old Chatbox localStorage dump). Any other shape — a primitive, null, or an array — is rejected before migration runs.

Source

Thrown at src/renderer/packages/backup/legacy-import.ts:42

function normalizeMeta(value: unknown): SessionMetaRecord | undefined {
  if (!value || typeof value !== 'object' || !('id' in value) || typeof value.id !== 'string') return undefined
  const candidate = {
    ...value,
    sortOrder: 'sortOrder' in value && typeof value.sortOrder === 'number' ? value.sortOrder : Date.now(),
    createdAt: 'createdAt' in value && typeof value.createdAt === 'number' ? value.createdAt : Date.now(),
  }
  const parsed = SessionMetaRecordSchema.safeParse(candidate)
  return parsed.success ? parsed.data : undefined
}

export async function importLegacyJsonBackup(
  file: File,
  options: LegacyBackupImportOptions
): Promise<LegacyBackupImportResult> {
  const parsed: unknown = JSON.parse(await file.text())
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error('Unsupported legacy backup data format')
  }
  const importData = parsed as Record<string, unknown>
  await options.migrateData({
    getData: <T>(key: string, defaultValue: T) => Promise.resolve((importData[key] ?? defaultValue) as T),
    setData: (key, value) => {
      importData[key] = value
      return Promise.resolve()
    },
    setAll: (data) => {
      Object.assign(importData, data)
      return Promise.resolve()
    },
  })

  const entriesToImport = Object.entries(importData).filter(
    ([key]) =>
      key !== BackupStorageKey.ChatSessionsList && key !== BackupStorageKey.ConfigVersion && !key.startsWith('__')
  )

View on GitHub (pinned to 81571269ad)

Solutions

  1. Open the file and confirm the top-level value is an object (starts with '{', not '[' or a primitive).
  2. Route v2 backups (.cbx / zip) to the v2 importer, not importLegacyJsonBackup.
  3. Re-export the data from a working Chatbox install if the file is corrupt.
  4. If importing arbitrary user JSON, pre-check that file.text() parses to a non-array object before calling the importer.

Example fix

// before
await importLegacyJsonBackup(file, options)

// after
const text = await file.text()
const probe: unknown = JSON.parse(text)
if (!probe || typeof probe !== 'object' || Array.isArray(probe)) {
  throw new Error('Please select a legacy Chatbox JSON backup')
}
await importLegacyJsonBackup(file, options)
Defensive patterns

Strategy: try-catch

Validate before calling

async function looksLikeLegacyBackup(file: File): Promise<boolean> {
  try {
    const value: unknown = JSON.parse(await file.text())
    return !!value && typeof value === 'object' && !Array.isArray(value)
  } catch {
    return false
  }
}

Type guard

const isRecord = (v: unknown): v is Record<string, unknown> =>
  !!v && typeof v === 'object' && !Array.isArray(v)

Try / catch

try {
  const result = await importLegacyJsonBackup(file, options)
} catch (error) {
  notifyUser((error as Error).message) // e.g. 'Unsupported legacy backup data format'
}

Prevention

When it happens

Trigger: Calling importLegacyJsonBackup with a File whose text is 'null', an array '[...]', a quoted string, a number/boolean, or an empty document. Also when a v2 .cbx zip or a settings-only JSON array is routed to the legacy importer.

Common situations: User picks a non-backup JSON file in the import dialog; the legacy export was an array of sessions in an older build; the file was truncated to empty; a v2 backup is mis-routed to the legacy path.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/545659385a7a4061. Report an issue: GitHub.