{"record":{"id":"065577e74a9ed934","repo":"github/spec-kit","slug":"no-bundle-yml-found-in-candidate","errorCode":null,"errorMessage":"No bundle.yml found in '{candidate}'.","messagePattern":"No bundle\\.yml found in '(.+?)'\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/commands/bundle/__init__.py","lineNumber":771,"sourceCode":"\n\ndef _local_manifest_source(arg: str):\n    \"\"\"Return a :class:`BundleManifest` if *arg* points at a local bundle.\n\n    Supports a built ``.zip`` artifact, a bundle directory, or a ``bundle.yml``\n    file. Returns ``None`` when *arg* is not an existing path, so callers fall\n    back to catalog-stack resolution by bundle id.\n    \"\"\"\n    from ...bundler.models.manifest import BundleManifest\n\n    candidate = Path(arg).expanduser()\n    if not candidate.exists():\n        return None\n\n    if candidate.is_dir():\n        manifest_path = candidate / \"bundle.yml\"\n        if not manifest_path.exists():\n            raise BundlerError(f\"No bundle.yml found in '{candidate}'.\")\n        return BundleManifest.from_file(manifest_path)\n\n    if candidate.suffix == \".zip\":\n        import yaml as _yaml\n\n        from ..._download_security import open_zip_bounded, read_zip_member_limited\n\n        with open_zip_bounded(candidate, error_type=BundlerError) as archive:\n            try:\n                archive.getinfo(\"bundle.yml\")\n            except KeyError as exc:\n                raise BundlerError(\n                    f\"Artifact '{candidate}' does not contain a bundle.yml.\"\n                ) from exc\n            raw = read_zip_member_limited(\n                archive,\n                \"bundle.yml\",\n                error_type=BundlerError,","sourceCodeStart":753,"sourceCodeEnd":789,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/commands/bundle/__init__.py#L753-L789","documentation":"_local_manifest_source(arg) treats an existing directory argument as a bundle directory and requires <dir>/bundle.yml. This branch fired because the directory exists but bundle.yml does not, and it raises (rather than returning None) precisely because the user clearly intended a local bundle at that path.","triggerScenarios":"specify bundle install ./my-bundle where ./my-bundle exists but the manifest is named bundle.yaml/manifest.yml, lives deeper, or is missing entirely.","commonSituations":"Renamed bundle.yml to bundle.yaml; cloned a repo whose bundle.yml is gitignored; pointed at the workspace root instead of the bundle's folder.","solutions":["Create or rename the manifest so <dir>/bundle.yml exists (exact name, .yml not .yaml).","Pass the manifest file directly: specify bundle install ./my-bundle/bundle.yml.","Check git status / .gitignore if the file should have been checked out."],"exampleFix":"# before\nspecify bundle install ./my-bundle   # contains bundle.yaml\n\n# after\nspecify bundle install ./my-bundle/bundle.yaml","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ncandidate = Path(arg).expanduser()\nif candidate.is_dir() and not (candidate / \"bundle.yml\").exists():\n    raise SystemExit(f\"{candidate} lacks bundle.yml; pass the manifest path or fix naming\")","typeGuard":"def is_bundle_dir(path: Path) -> bool:\n    return path.is_dir() and (path / \"bundle.yml\").is_file()","tryCatchPattern":"try:\n    manifest = _local_manifest_source(arg)\nexcept BundlerError as exc:\n    if \"No bundle.yml found in\" in str(exc):\n        # pass <dir>/bundle.yml or an unambiguous manifest path\n        ...","preventionTips":["Name bundle manifests bundle.yml exactly.","Pass the manifest file path when the directory layout is unusual."],"tags":["bundler","manifest","filesystem","path"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}