{"record":{"id":"9f9defce29968773","repo":"HKUDS/Vibe-Trading","slug":"playbook-self-slug-r-has-no-variable-key-r-de","errorCode":null,"errorMessage":"playbook {self.slug!r} has no variable {key!r}; declared: {sorted(values)}","messagePattern":"playbook (.+?) has no variable (.+?); declared: (.+?)","errorType":"validation","errorClass":"PlaybookError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/playbooks.py","lineNumber":128,"sourceCode":"    def render(self, variables: Optional[Mapping[str, Any]] = None) -> str:\n        \"\"\"Return the instruction body with placeholders substituted.\n\n        Args:\n            variables: Overrides keyed by declared variable name. A key that\n                is not declared by this playbook is an error rather than a\n                silent no-op. A blank value falls back to the declared default.\n\n        Returns:\n            The prompt text to hand to the scheduler.\n\n        Raises:\n            PlaybookError: If an undeclared variable is supplied or a value\n                exceeds the length cap.\n        \"\"\"\n        values = dict(self.variables)\n        for key, raw in (variables or {}).items():\n            if key not in values:\n                raise PlaybookError(\n                    f\"playbook {self.slug!r} has no variable {key!r}; declared: {sorted(values)}\"\n                )\n            text = str(raw).strip()\n            if len(text) > _MAX_VARIABLE_CHARS:\n                raise PlaybookError(\n                    f\"playbook variable {key!r} is {len(text)} chars; the cap is {_MAX_VARIABLE_CHARS}\"\n                )\n            if text:\n                values[key] = text\n        # Every placeholder in the body was checked against ``variables`` at\n        # load time, so this substitution cannot leave one behind.\n        return _PLACEHOLDER_RE.sub(lambda m: values[m.group(1)], self.body)\n\n    def to_job(\n        self,\n        *,\n        job_id: Optional[str] = None,\n        schedule: Optional[str] = None,","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/playbooks.py#L110-L146","documentation":"Raised by Playbook.render (agent/src/scheduled_research/playbooks.py:128) as PlaybookError when the variables dict passed to render contains a key that the playbook's frontmatter did not declare. Render only fills declared variables, so supplying an extra key is treated as a caller bug and rejected with the declared key list in the message.","triggerScenarios":"Calling to_job or build_job_from_draft with variables={'topic': 'x', 'depth': 2} when the playbook only declares 'topic'. The message lists sorted declared keys, e.g. declared: ['topic'].","commonSituations":"Renaming a playbook variable in its markdown file but not in callers; sharing one variables dict across multiple playbooks with different declarations; passing config keys (like timezone or model) inside the variables mapping.","solutions":["Check the message's 'declared:' list and remove/rename the extra key","Keep per-playbook variables dicts rather than one shared mapping","Re-read the playbook frontmatter after edits and update callers","Separate config fields from template variables at the call site"],"exampleFix":"# before\njob = playbook.to_job(schedule=\"@daily\", variables={\"topic\": \"AI\", \"extra\": 1})\n\n# after\njob = playbook.to_job(schedule=\"@daily\", variables={\"topic\": \"AI\"})","handlingStrategy":"validation","validationCode":"def filter_declared(playbook, variables):\n    declared = set(playbook.variables)\n    return {k: v for k, v in (variables or {}).items() if k in declared}","typeGuard":"def only_declared_variables(playbook, variables):\n    \"\"\"Return variables restricted to the playbook's declared keys.\"\"\"\n    declared = set(playbook.variables)\n    extras = set(variables or {}) - declared\n    if extras:\n        raise ValueError(f\"undeclared variables: {sorted(extras)}; declared: {sorted(declared)}\")\n    return dict(variables)","tryCatchPattern":"from agent.src.scheduled_research.playbooks import PlaybookError\n\ntry:\n    job = playbook.to_job(schedule=\"@daily\", variables=vars)\nexcept PlaybookError as exc:\n    if \"has no variable\" in str(exc):\n        vars = {k: v for k, v in vars.items() if k in playbook.variables}\n        job = playbook.to_job(schedule=\"@daily\", variables=vars)\n    else:\n        raise","preventionTips":["Derive variable dicts from playbook.variables, not a shared global config","Regenerate/review callers after editing playbook frontmatter","Keep template variables separate from job config keys"],"tags":["playbook","template-variables","validation","scheduled-research"],"backgroundTag":"undeclared-template-variable","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}