{"record":{"id":"8bf1abea324f11b4","repo":"unionlabs/union","slug":"blog-entry-item-sys-id-has-no-slug","errorCode":null,"errorMessage":"Blog entry ${item.sys.id} has no slug","messagePattern":"Blog entry (.+?) has no slug","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"site/scripts/import-contentful-export.mjs","lineNumber":513,"sourceCode":"    `updatedAt: ${yamlString(metadata.updatedAt)}`,\n    \"---\",\n    \"\",\n  ].join(\"\\n\")\n  const body = renderNode(field(item, \"copy\"), { terms: true })\n  await writeFile(\n    join(legalDirectory, `${slug}.md`),\n    `${frontmatter}${body}\\n`,\n  )\n}\n\nfor (\n  const item of preview.entries.items.filter(\n    (item) => entryType(item) === \"blog\",\n  )\n) {\n  const slug = field(item, \"slug\")\n  if (!slug) {\n    throw new Error(`Blog entry ${item.sys.id} has no slug`)\n  }\n\n  const metadata = sourceMetadata(item)\n  const cover = assetFromLink(field(item, \"cover\"))\n  const frontmatter = [\n    \"---\",\n    `title: ${yamlString(field(item, \"title\") || \"\")}`,\n    `date: ${yamlString(field(item, \"date\") || \"\")}`,\n    `author: ${yamlString(field(item, \"author\") || \"union_build\")}`,\n    `description: ${yamlString(field(item, \"description\") || \"\")}`,\n    `cover: ${yamlString(cover?.path || \"\")}`,\n    `coverAlt: ${yamlString(cover?.description || cover?.title || \"\")}`,\n    `hidden: ${field(item, \"hidden\") === true}`,\n    `published: ${metadata.published}`,\n    `sourceId: ${yamlString(metadata.sourceId)}`,\n    `createdAt: ${yamlString(metadata.createdAt)}`,\n    `updatedAt: ${yamlString(metadata.updatedAt)}`,\n    \"---\",","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/site/scripts/import-contentful-export.mjs#L495-L531","documentation":"Every entry of content type \"blog\" must expose a non-empty localized slug because the slug becomes the markdown filename under site/src/content/blog (join(blogDirectory, `${slug}.md`)). field() reads fields.slug[\"en-US\"]; a blog entry whose en-US slug is absent makes the import abort with the entry's sys.id.","triggerScenarios":"A blog entry saved without a slug (the Contentful model does not enforce it), a slug entered under a locale other than en-US, or a draft/placeholder blog entry present in the preview export.","commonSituations":"Editors leaving slug empty on drafts that still land in the preview export; multilingual spaces with the slug filled only in another locale; entries created via the Content API without the slug field; content model where slug is optional.","solutions":["Look up the reported sys.id in Contentful, fill in the slug, and re-export","Confirm the slug value is in the en-US locale (field() reads item.fields.slug[\"en-US\"])","If draft noise is the cause, publish/complete or remove the placeholder entries and re-export","Keep slugs unique — duplicate slugs overwrite each other's markdown files"],"exampleFix":"// before\nconst slug = field(item, \"slug\")\nif (!slug) {\n  throw new Error(`Blog entry ${item.sys.id} has no slug`)\n}\n\n// after — report every offender in one pass instead of only the first\nconst blogs = preview.entries.items.filter((item) => entryType(item) === \"blog\")\nconst missing = blogs.filter((item) => !field(item, \"slug\")).map((item) => item.sys.id)\nif (missing.length > 0) {\n  throw new Error(`Blog entries without an en-US slug: ${missing.join(\", \")}`)\n}","handlingStrategy":"validation","validationCode":"# List blog entries lacking an en-US slug before importing\njq -r '.items[] | select(.sys.contentType.sys.id == \"blog\") | select((.fields.slug // {})[\"en-US\"] // \"\" | length == 0) | .sys.id' \"<preview-dir>/entries.json\"","typeGuard":"const hasSlug = (item) => Boolean(item?.fields?.slug?.[\"en-US\"])","tryCatchPattern":null,"preventionTips":["Make slug required in the Contentful blog content model","Always fill the slug in the en-US locale the script reads","Keep slugs unique — they become markdown filenames that silently overwrite on collision","Re-export after fixing entries; the script only reads the export files"],"tags":["contentful","content-import","slug","javascript"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}