{"record":{"id":"96ac1b8b8874d8ea","repo":"Budibase/budibase","slug":"project-package-contains-a-doc-without-an-id","errorCode":null,"errorMessage":"Project package contains a doc without an id.","messagePattern":"Project package contains a doc without an id\\.","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/projects/backups/imports.ts","lineNumber":341,"sourceCode":"const RESOURCE_ID_PREFIXES: Record<ResourceType, string[]> = {\n  [ResourceType.PROJECT]: [prefixed(DocumentType.PROJECT)],\n  [ResourceType.AGENT]: [prefixed(DocumentType.AGENT)],\n  [ResourceType.DATASOURCE]: [\n    prefixed(DocumentType.DATASOURCE),\n    prefixed(DocumentType.DATASOURCE_PLUS),\n  ],\n  [ResourceType.TABLE]: [prefixed(DocumentType.TABLE)],\n  [ResourceType.ROW_ACTION]: [prefixed(DocumentType.ROW_ACTIONS)],\n  [ResourceType.QUERY]: [prefixed(DocumentType.QUERY)],\n  [ResourceType.AUTOMATION]: [prefixed(DocumentType.AUTOMATION)],\n  [ResourceType.WORKSPACE_APP]: [prefixed(DocumentType.WORKSPACE_APP)],\n  [ResourceType.SCREEN]: [prefixed(DocumentType.SCREEN)],\n}\n\nconst validateDocMatchesPath = (importedDoc: ImportedDoc) => {\n  const id = importedDoc.doc._id\n  if (!id) {\n    throw new HTTPError(\"Project package contains a doc without an id.\", 400)\n  }\n\n  const expectedFileName = `${id}.json`\n  if (basename(importedDoc.path) !== expectedFileName) {\n    throw new HTTPError(`Project package doc path does not match '${id}'.`, 400)\n  }\n\n  const validPrefixes = RESOURCE_ID_PREFIXES[importedDoc.resourceType]\n  if (!validPrefixes.some(prefix => id.startsWith(prefix))) {\n    throw new HTTPError(\n      `Project package doc '${id}' does not match resource type '${importedDoc.resourceType}'.`,\n      400\n    )\n  }\n}\n\nconst remapObjectKeys = <T>(\n  value: Record<string, T>,","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/projects/backups/imports.ts#L323-L359","documentation":"validateDocMatchesPath checks each imported doc parsed from the package. If the parsed JSON document has no `_id` field, this HTTPError (400) is thrown — every doc in a project package must be a persisted Budibase document with an id.","triggerScenarios":"A .json file in the docs directory whose parsed object lacks `_id` — e.g. hand-authored config files, schema-only files, null/empty JSON, or an array root instead of a doc object placed in the docs tree.","commonSituations":"Manually editing package JSON and deleting the _id; tools exporting partial docs without ids; placing arbitrary config JSON (e.g. datasource settings snippets) into the docs directory; corrupted export where doc fields were dropped.","solutions":["Open the offending .json in the archive and ensure it contains a valid `_id` field (matching its filename, e.g. `ta_abc123.json` → `_id: \"ta_abc123\"`).","Re-export the project from Budibase instead of hand-assembling docs.","Remove any non-document JSON files from the docs directory.","Validate all docs before packaging: every file must be an object with `_id`, and filename must equal `${_id}.json`."],"exampleFix":"// before: ta_abc123.json\n{ \"name\": \"My Table\", \"type\": \"table\" }\n// after\n{ \"_id\": \"ta_abc123\", \"name\": \"My Table\", \"type\": \"table\" }","handlingStrategy":"type-guard","validationCode":"import { readFileSync, readdirSync } from \"fs\"\nimport { join, basename } from \"path\"\n\nfunction allDocsHaveIds(docsDir: string): boolean {\n  for (const f of readdirSync(docsDir, { recursive: true })) {\n    if (!String(f).endsWith(\".json\")) continue\n    const doc = JSON.parse(readFileSync(join(docsDir, String(f)), \"utf8\"))\n    if (typeof doc?._id !== \"string\" || doc._id.length === 0) return false\n  }\n  return true\n}","typeGuard":"interface BudibaseDoc { _id: string }\n\nfunction isDocWithId(value: unknown): value is BudibaseDoc {\n  return (\n    typeof value === \"object\" && value !== null && !Array.isArray(value) &&\n    typeof (value as { _id?: unknown })._id === \"string\"\n  )\n}","tryCatchPattern":"try {\n  await api.importProjectPackage(file)\n} catch (err) {\n  if (err?.status === 400 && err.message === \"Project package contains a doc without an id.\") {\n    // find the .json missing _id and add it or remove the file\n  } else throw err\n}","preventionTips":["Never delete `_id` when hand-editing package docs","Every doc file must be an object with a string `_id` equal to its filename minus .json","Re-export from Budibase rather than hand-assembling docs","Lint all package JSON for `_id` presence before packaging"],"tags":["http-400","json-validation","import","missing-id"],"backgroundTag":"missing-document-id","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}