{"record":{"id":"1b91eeb97dd163b0","repo":"github/spec-kit","slug":"refusing-to-build-an-invalid-manifest-run-specif","errorCode":null,"errorMessage":"Refusing to build an invalid manifest. Run 'specify bundle validate' and fix:\n  - {report.errors}","messagePattern":"Refusing to build an invalid manifest\\. Run 'specify bundle validate' and fix:\n  - (.+?)","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/services/packager.py","lineNumber":54,"sourceCode":"    output_dir: Path | None = None,\n) -> BuildResult:\n    bundle_dir = Path(bundle_dir).resolve()\n    manifest_path = bundle_dir / \"bundle.yml\"\n    if not manifest_path.exists():\n        raise BundlerError(f\"No bundle.yml found in '{bundle_dir}'.\")\n\n    # The artifact contract requires a human-facing README.md alongside the\n    # manifest; refuse early rather than publish a bundle with no description.\n    if not (bundle_dir / \"README.md\").exists():\n        raise BundlerError(\n            f\"No README.md found in '{bundle_dir}'. Every bundle must ship a \"\n            \"README.md describing it.\"\n        )\n\n    manifest = BundleManifest.from_file(manifest_path)\n    report = validate_manifest(manifest)\n    if not report.ok:\n        raise BundlerError(\n            \"Refusing to build an invalid manifest. Run 'specify bundle validate' \"\n            \"and fix:\\n  - \" + \"\\n  - \".join(report.errors)\n        )\n\n    out_dir = Path(output_dir).resolve() if output_dir else bundle_dir\n    out_dir.mkdir(parents=True, exist_ok=True)\n    artifact_name = f\"{manifest.bundle.id}-{manifest.bundle.version}.zip\"\n    artifact_path = out_dir / artifact_name\n    # Defense in depth: even though validate_manifest() rejects unsafe ids, make\n    # sure a crafted id cannot push the artifact outside the output directory.\n    ensure_within(out_dir, artifact_path)\n\n    # If the output dir lives inside the bundle, skip its whole subtree so\n    # previously-built artifacts are never re-packaged (keeps builds\n    # reproducible and bounded).\n    skip_dir = out_dir if out_dir != bundle_dir and _is_within(bundle_dir, out_dir) else None\n    # Also skip any prior build artifact for this bundle (e.g. an older\n    # <id>-<version>.zip sitting next to bundle.yml), not just the current one.","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/services/packager.py#L36-L72","documentation":"build_bundle() ran validate_manifest() on the parsed manifest and it reported one or more errors, so the build is refused. The message embeds every validation error as a bullet list and points at `specify bundle validate` for the interactive path. No zip artifact is created.","triggerScenarios":"A bundle.yml with schema violations: missing required fields (id/version/components), malformed component refs, invalid versions, or unsafe ids that validate_manifest() rejects. The check fires before out_dir.mkdir and zip creation.","commonSituations":"Hand-edited manifest with a typo or wrong indentation; component entry referencing an unknown kind; version string not matching the expected format; bundle authored against an older schema after a spec-kit upgrade.","solutions":["Run `specify bundle validate <bundle_dir>` and fix each listed error — the bullets in this same message are the validator output.","Check required fields exist: bundle id, version, and a well-formed components list.","Compare against a known-good bundle.yml from an existing bundle or the template.","If the error names a component kind, use only kinds the bundler supports (presets, extensions, workflows, steps)."],"exampleFix":"# before (bundle.yml)\nbundle:\n  id: my-bundle\n  # version missing\n\n# after\nbundle:\n  id: my-bundle\n  version: 1.0.0","handlingStrategy":"validation","validationCode":"from specify_cli.bundler.services.packager import build_bundle\nfrom specify_cli.bundler.core import BundleManifest\nfrom specify_cli.bundler.validation import validate_manifest  # adjust import to repo layout\n\nreport = validate_manifest(BundleManifest.from_file(bundle_dir / \"bundle.yml\"))\nif not report.ok:\n    print(\"\\n  - \".join(report.errors)); exit(1)\nbuild_bundle(bundle_dir)","typeGuard":null,"tryCatchPattern":"from specify_cli.bundler.core import BundlerError\n\ntry:\n    build_bundle(bundle_dir)\nexcept BundlerError as exc:\n    if \"invalid manifest\" in str(exc):\n        # exc message already lists every validator error line by line\n        log.error(\"manifest invalid:\\n%s\", exc)","preventionTips":["Run `specify bundle validate` in CI for every bundle directory before build.","Validate after every hand-edit of bundle.yml, not just before release.","Copy a known-good bundle.yml as the schema reference when authoring."],"tags":["bundler","build","validation","manifest"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}