HKUDS/Vibe-Trading · error · PlaybookError

playbook {path}: {key!r} must be a non-empty list of strings

Error message

playbook {path}: {key!r} must be a non-empty list of strings

What it means

Raised by _string_list (agent/src/scheduled_research/playbooks.py:243) as PlaybookError when a frontmatter field that must be a non-empty list of strings is neither a string nor a non-empty list. A bare string is coerced to a one-element list; anything else (dict, int, null, empty list) fails with the playbook path and key name in the message.

Source

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

            "name": self.name,
            "description": self.description,
            "suggested_schedule": self.suggested_schedule,
            "suggested_timezone": self.suggested_timezone,
            "markets": list(self.markets),
            "data_capabilities": list(self.data_capabilities),
            "variables": dict(self.variables),
        }
        if include_body:
            data["body"] = self.body
        return data


def _string_list(value: Any, key: str, path: Path) -> Tuple[str, ...]:
    """Coerce a frontmatter value to a tuple of non-empty strings."""
    if isinstance(value, str):
        value = [value]
    if not isinstance(value, list) or not value:
        raise PlaybookError(f"playbook {path}: {key!r} must be a non-empty list of strings")
    items = tuple(str(item).strip() for item in value)
    if any(not item for item in items):
        raise PlaybookError(f"playbook {path}: {key!r} contains an empty entry")
    return items


def load_playbook_file(path: Path) -> ResearchPlaybook:
    """Parse one playbook markdown file.

    Args:
        path: Path to a ``.md`` playbook file.

    Returns:
        The parsed :class:`ResearchPlaybook`.

    Raises:
        PlaybookError: If the filename is not a valid slug, the file has no
            frontmatter, is missing a required key, declares a malformed

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Fix the frontmatter so the field is a list of strings: tags: [news]
  2. For an optional field, remove the key entirely rather than leaving it empty
  3. Remember a single string is accepted and coerced — wrap multi-value entries in a YAML list
  4. Run load_playbook_file in CI/lint over all playbooks to catch this early

Example fix

# before (frontmatter)
tags:

# after
tags: [news, research]
Defensive patterns

Strategy: validation

Validate before calling

def string_list_ok(value):
    if isinstance(value, str):
        return bool(value.strip())
    return isinstance(value, list) and len(value) > 0 and all(
        isinstance(i, str) and i.strip() for i in value
    )

Type guard

from typing import Any, Tuple

def as_string_list(value: Any) -> Tuple[str, ...]:
    if isinstance(value, str):
        value = [value]
    if not isinstance(value, list) or not value:
        return ()
    return tuple(str(i).strip() for i in value if str(i).strip())

Try / catch

from agent.src.scheduled_research.playbooks import PlaybookError, load_playbook_file

try:
    playbook = load_playbook_file(path)
except PlaybookError as exc:
    if "must be a non-empty list of strings" in str(exc):
        log.warning("skipping misconfigured playbook %s: %s", path, exc)
        playbook = None
    else:
        raise

Prevention

When it happens

Trigger: load_playbook_file on a playbook whose frontmatter has e.g. variables: {} instead of a list, tags: (empty), or tags: 3. YAML flow like 'tags:' with nothing after it parses as None and hits this too.

Common situations: Editing playbook markdown frontmatter and leaving a key empty; YAML indentation making an intended list parse as a scalar or dict; copy-pasting frontmatter between playbooks with different field shapes.

Related errors


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