{"record":{"id":"8f09b093de46c792","repo":"calesthio/OpenMontage","slug":"playbook-not-found-name","errorCode":null,"errorMessage":"Playbook not found: {name}","messagePattern":"Playbook not found: (.+?)","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"lib/playbook_generator.py","lineNumber":39,"sourceCode":"    / \"schemas\" / \"styles\" / \"playbook.schema.json\"\n)\nSTYLES_DIR = Path(__file__).resolve().parent.parent / \"styles\"\nCUSTOM_STYLES_DIR = STYLES_DIR / \"custom\"\n\n\ndef _load_playbook_schema() -> dict:\n    with open(PLAYBOOK_SCHEMA_PATH) as f:\n        return json.load(f)\n\n\ndef load_existing_playbook(name: str) -> dict[str, Any]:\n    \"\"\"Load an existing playbook YAML by name.\"\"\"\n    path = STYLES_DIR / f\"{name}.yaml\"\n    if not path.exists():\n        # Check custom directory\n        path = CUSTOM_STYLES_DIR / f\"{name}.yaml\"\n    if not path.exists():\n        raise FileNotFoundError(f\"Playbook not found: {name}\")\n    with open(path) as f:\n        return yaml.safe_load(f)\n\n\ndef list_playbooks() -> list[str]:\n    \"\"\"List all available playbook names (preset + custom).\"\"\"\n    names = [p.stem for p in STYLES_DIR.glob(\"*.yaml\")]\n    if CUSTOM_STYLES_DIR.exists():\n        names.extend(p.stem for p in CUSTOM_STYLES_DIR.glob(\"*.yaml\"))\n    return sorted(set(names))\n\n\ndef generate_playbook(\n    name: str,\n    context: dict[str, Any],\n    base_playbook: str | None = None,\n) -> dict[str, Any]:\n    \"\"\"Generate a custom playbook from production context.","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/lib/playbook_generator.py#L21-L57","documentation":"Raised by lib/playbook_generator.py's load_existing_playbook() when no {name}.yaml exists in either the built-in styles directory (STYLES_DIR) or the custom styles directory (CUSTOM_STYLES_DIR). Playbooks are resolved by name with a .yaml extension appended, checked against the preset directory first, then the custom directory. list_playbooks() returns the exact valid names (preset + custom, deduplicated and sorted).","triggerScenarios":"Calling load_existing_playbook with a typo'd name, wrong case, or a name that includes the .yaml suffix (making it look for name.yaml.yaml); referencing a custom playbook whose YAML was moved or never created in CUSTOM_STYLES_DIR.","commonSituations":"Typo in a config or CLI reference; custom playbook file saved in the wrong directory; playbook renamed between versions; case-sensitive mismatch on case-sensitive filesystems.","solutions":["Call list_playbooks() and copy the exact name (without extension).","If it's a custom playbook, confirm the YAML file sits in CUSTOM_STYLES_DIR and the name matches including case.","Pass the bare name without the .yaml extension."],"exampleFix":"# before\npb = load_existing_playbook(\"Cinematic.yaml\")  # FileNotFoundError\n\n# after\nfrom lib.playbook_generator import list_playbooks, load_existing_playbook\nprint(list_playbooks())\npb = load_existing_playbook(\"cinematic\")","handlingStrategy":"validation","validationCode":"from lib.playbook_generator import list_playbooks\n\navailable = set(list_playbooks())\nif playbook_name not in available:\n    raise SystemExit(f\"Unknown playbook {playbook_name!r}. Available: {sorted(available)}\")","typeGuard":"from lib.playbook_generator import list_playbooks\n\ndef playbook_exists(name: str) -> bool:\n    return name in set(list_playbooks())","tryCatchPattern":"try:\n    playbook = load_existing_playbook(name)\nexcept FileNotFoundError as e:\n    raise SystemExit(f\"{e}. Run list_playbooks() to see valid names.\") from e","preventionTips":["Use bare names without the .yaml extension.","Place custom playbooks in CUSTOM_STYLES_DIR, not arbitrary folders.","Names are case-sensitive; match the file stem exactly."],"tags":["playbook","file-not-found","configuration","styles"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}