nexu-io/open-design · error · ValueError

slide_plan must be a non-empty JSON array of slides

Error message

slide_plan must be a non-empty JSON array of slides

What it means

Raised by load_plan in preview_outline_html.py when the JSON file at the given path does not reduce to a non-empty list of slides. The function accepts either a bare JSON array of slides OR an object with a `slides` key (a common wrapper shape), but after unwrapping the result must be a list with at least one element. Anything else — an object without `slides`, an empty array, a scalar, null — raises ValueError.

Source

Thrown at plugins/community/humanize-ppt/scripts/preview_outline_html.py:66

    "takeaway": "收束",
}

ROLE_LEAVE_STATE = {
    "hook": "注意力被抓住,愿意继续听",
    "context": "共享背景成立,知道为什么是现在",
    "tension": "意识到旧理解有缺口,想要答案",
    "method": "看到一条可执行的路径",
    "proof": "相信路径真实有效,不是口号",
    "takeaway": "带走一句可复述的判断,知道下一步",
}


def load_plan(path):
    data = json.loads(Path(path).read_text(encoding="utf-8"))
    if isinstance(data, dict) and isinstance(data.get("slides"), list):
        data = data["slides"]
    if not isinstance(data, list) or not data:
        raise ValueError("slide_plan must be a non-empty JSON array of slides")
    return data


def state_rows(plan):
    """Chain per-slide enter/leave audience states from the role sequence."""
    rows = []
    enter = DECK_INITIAL_STATE
    for slide in plan:
        role = slide.get("role", "slide")
        leave = ROLE_LEAVE_STATE.get(role, enter)
        rows.append({
            "slide_id": slide.get("slide_id", "?"),
            "role": role,
            "role_label": ROLE_LABELS.get(role, role),
            "title": slide.get("title", ""),
            "message": slide.get("message", ""),
            "speaker_intent": slide.get("speaker_intent", ""),
            "enter": enter,

View on GitHub (pinned to 5be4028344)

Solutions

  1. Open the JSON file and confirm its top-level shape: `python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(type(d), len(d) if isinstance(d,(list,dict)) else '')" plan.json`.
  2. If the file is an object, ensure it has a non-empty `slides` array; if it has slides under a different key, restructure to `{"slides":[...]}` or a bare `[...]`.
  3. Re-run the upstream planner so it emits a non-empty array of slide objects.
  4. Validate the plan with a one-liner before invoking preview_outline_html: assert it is a list with len >= 1 and each entry is a dict.

Example fix

// before (plan.json contains {})
python3 preview_outline_html.py --plan plan.json
# -> ValueError: slide_plan must be a non-empty JSON array of slides

// after
# plan.json:
[{"slide_id":"s1","role":"hook","title":"..."}, {"slide_id":"s2","role":"tension","title":"..."}]
python3 preview_outline_html.py --plan plan.json
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

def load_plan_checked(path: str):
    data = json.loads(Path(path).read_text(encoding="utf-8"))
    if isinstance(data, dict) and isinstance(data.get("slides"), list):
        data = data["slides"]
    if not isinstance(data, list) or not data:
        raise SystemExit(
            f"Plan must be a non-empty array of slides (got "
            f"{type(data).__name__}, len={len(data) if hasattr(data,'__len__') else 'n/a'})."
        )
    if not all(isinstance(s, dict) for s in data):
        raise SystemExit("Each slide must be a JSON object.")
    return data

Type guard

def is_slide_plan(data) -> bool:
    if isinstance(data, dict):
        data = data.get("slides")
    return isinstance(data, list) and len(data) > 0 and all(isinstance(s, dict) for s in data)

Try / catch

try:
    plan = load_plan(plan_path)
except ValueError as exc:
    raise SystemExit(f"Invalid slide plan: {exc}") from exc

Prevention

When it happens

Trigger: Passing a JSON file that is `{}` or `{"meta": ...}` without a slides array; an empty `[]`; a file containing a single slide object instead of an array; malformed/placeholder JSON produced by a half-finished pipeline stage; wrong file handed to the script (a config JSON instead of a plan JSON).

Common situations: Upstream slide-planning step wrote a status object instead of a plan; LLM produced a single slide dict rather than an array; user passed the deck spec where the outline plan was expected; empty plan from a filtering step that removed every slide.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/278a09e3a81eaf0f. Report an issue: GitHub.