linshenkx/prompt-optimizer · error · Error

Invalid app backup package manifest

Error message

Invalid app backup package manifest

What it means

Thrown by parseManifest (called from readDataManagerResourcePackage in packages/ui/src/utils/data-manager-resource-package.ts) when the unzipped backup's manifest.json fails structural validation: schemaVersion must equal DATA_MANAGER_RESOURCE_PACKAGE_SCHEMA_VERSION, appDataPath must equal APP_DATA_JSON_PATH, favoritesPath must equal FAVORITES_JSON_PATH, and resources/missingResources must be arrays. It means the zip is not a valid app backup package produced by this code's exporter. It protects the restore path from partially written or foreign archives.

Solutions

  1. Verify the package was exported by the same app version (or a version sharing the same DATA_MANAGER_RESOURCE_PACKAGE_SCHEMA_VERSION); re-export the backup from the source app
  2. Unzip the archive and inspect manifest.json: confirm schemaVersion, appDataPath and favoritesPath values and that resources/missingResources are arrays
  3. If you maintain the exporter, regenerate the package rather than hand-editing the manifest
  4. If supporting cross-version restores, add a migration step that upgrades older manifests to the current schemaVersion before validation

Example fix

// before
const pkg = readDataManagerResourcePackage(bytes) // throws: Invalid app backup package manifest

// after
const manifest = JSON.parse(strFromU8(unzipSync(bytes)['manifest.json']))
if (manifest.schemaVersion !== DATA_MANAGER_RESOURCE_PACKAGE_SCHEMA_VERSION) {
  throw new Error(`Unsupported backup schema version: ${manifest.schemaVersion}`)
}
const pkg = readDataManagerResourcePackage(bytes)
Defensive patterns

Strategy: validation

Validate before calling

import { unzipSync } from 'fflate'
import { strFromU8 } from 'fflate'

const isValidAppBackupManifest = (json: string): boolean => {
  try {
    const m = JSON.parse(json)
    return (
      typeof m === 'object' && m !== null &&
      m.schemaVersion === DATA_MANAGER_RESOURCE_PACKAGE_SCHEMA_VERSION &&
      m.appDataPath === 'app-data.json' &&
      m.favoritesPath === 'favorites.json' &&
      Array.isArray(m.resources) &&
      Array.isArray(m.missingResources)
    )
  } catch {
    return false
  }
}

// before readDataManagerResourcePackage:
const files = unzipSync(bytes)
const ok = !!files['manifest.json'] && isValidAppBackupManifest(strFromU8(files['manifest.json']))

Type guard

const isAppBackupManifest = (v: unknown): v is DataManagerResourcePackageManifest =>
  typeof v === 'object' && v !== null &&
  (v as DataManagerResourcePackageManifest).schemaVersion === DATA_MANAGER_RESOURCE_PACKAGE_SCHEMA_VERSION &&
  Array.isArray((v as DataManagerResourcePackageManifest).resources) &&
  Array.isArray((v as DataManagerResourcePackageManifest).missingResources)

Try / catch

try {
  const pkg = readDataManagerResourcePackage(bytes)
} catch (e) {
  if (e instanceof Error && e.message === 'Invalid app backup package manifest') {
    // show 'incompatible or corrupted backup' UI; do not retry
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling readDataManagerResourcePackage on a zip whose manifest.json has a mismatched schemaVersion (package written by an older/newer app version), wrong appDataPath/favoritesPath values, or missing/non-array resources or missingResources fields. Also triggered by a hand-edited or truncated manifest.json, or JSON.parse succeeding but yielding a non-record (e.g. an array or string).

Common situations: Restoring a backup created by a different app version after a schema migration; renaming the internal app-data.json/favorites.json files inside the zip; passing a generic zip that happens to contain a manifest.json; corrupting the manifest during download/transfer. Note JSON.parse failures throw a SyntaxError before this check, so this error specifically means parseable-but-invalid content.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/9e04e2746bcfb7d1. Report an issue: GitHub.

Appendix: source

Thrown at packages/ui/src/utils/data-manager-resource-package.ts:160

  ]
}

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

const textToZipBytes = (text: string): Uint8Array => copyBytes(strToU8(text))

const parseManifest = (json: string): DataManagerResourcePackageManifest => {
  const parsed = JSON.parse(json) as unknown
  if (
    !isRecord(parsed) ||
    parsed.schemaVersion !== DATA_MANAGER_RESOURCE_PACKAGE_SCHEMA_VERSION ||
    parsed.appDataPath !== APP_DATA_JSON_PATH ||
    parsed.favoritesPath !== FAVORITES_JSON_PATH ||
    !Array.isArray(parsed.resources) ||
    !Array.isArray(parsed.missingResources)
  ) {
    throw new Error('Invalid app backup package manifest')
  }
  return parsed as DataManagerResourcePackageManifest
}

const collectStoreResources = async (
  config: ImageStoreExportConfig,
  files: Record<string, Uint8Array>,
): Promise<{
  resources: DataManagerResourceManifestEntry[]
  missing: Array<{ store: DataManagerImageStoreKey; id: string }>
}> => {
  if (!config.service) {
    return { resources: [], missing: [] }
  }

  const metadataList = await config.service.listAllMetadata()
  const resources: DataManagerResourceManifestEntry[] = []
  const missing: Array<{ store: DataManagerImageStoreKey; id: string }> = []

View on GitHub (pinned to 3e677b1d9f)