HKUDS/Vibe-Trading · error · PlaybookError

playbook {self.slug!r} has no variable {key!r}; declared: {s

Error message

playbook {self.slug!r} has no variable {key!r}; declared: {sorted(values)}

What it means

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.

Source

Thrown at agent/src/scheduled_research/playbooks.py:128

    def render(self, variables: Optional[Mapping[str, Any]] = None) -> str:
        """Return the instruction body with placeholders substituted.

        Args:
            variables: Overrides keyed by declared variable name. A key that
                is not declared by this playbook is an error rather than a
                silent no-op. A blank value falls back to the declared default.

        Returns:
            The prompt text to hand to the scheduler.

        Raises:
            PlaybookError: If an undeclared variable is supplied or a value
                exceeds the length cap.
        """
        values = dict(self.variables)
        for key, raw in (variables or {}).items():
            if key not in values:
                raise PlaybookError(
                    f"playbook {self.slug!r} has no variable {key!r}; declared: {sorted(values)}"
                )
            text = str(raw).strip()
            if len(text) > _MAX_VARIABLE_CHARS:
                raise PlaybookError(
                    f"playbook variable {key!r} is {len(text)} chars; the cap is {_MAX_VARIABLE_CHARS}"
                )
            if text:
                values[key] = text
        # Every placeholder in the body was checked against ``variables`` at
        # load time, so this substitution cannot leave one behind.
        return _PLACEHOLDER_RE.sub(lambda m: values[m.group(1)], self.body)

    def to_job(
        self,
        *,
        job_id: Optional[str] = None,
        schedule: Optional[str] = None,

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Check the message's 'declared:' list and remove/rename the extra key
  2. Keep per-playbook variables dicts rather than one shared mapping
  3. Re-read the playbook frontmatter after edits and update callers
  4. Separate config fields from template variables at the call site

Example fix

# before
job = playbook.to_job(schedule="@daily", variables={"topic": "AI", "extra": 1})

# after
job = playbook.to_job(schedule="@daily", variables={"topic": "AI"})
Defensive patterns

Strategy: validation

Validate before calling

def filter_declared(playbook, variables):
    declared = set(playbook.variables)
    return {k: v for k, v in (variables or {}).items() if k in declared}

Type guard

def only_declared_variables(playbook, variables):
    """Return variables restricted to the playbook's declared keys."""
    declared = set(playbook.variables)
    extras = set(variables or {}) - declared
    if extras:
        raise ValueError(f"undeclared variables: {sorted(extras)}; declared: {sorted(declared)}")
    return dict(variables)

Try / catch

from agent.src.scheduled_research.playbooks import PlaybookError

try:
    job = playbook.to_job(schedule="@daily", variables=vars)
except PlaybookError as exc:
    if "has no variable" in str(exc):
        vars = {k: v for k, v in vars.items() if k in playbook.variables}
        job = playbook.to_job(schedule="@daily", variables=vars)
    else:
        raise

Prevention

When it happens

Trigger: 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'].

Common situations: 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.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/9f9defce29968773. Report an issue: GitHub.