nexu-io/open-design · error · SystemExit

[CompetitorsPlan] Invalid JSON: {exc}

Error message

[CompetitorsPlan] Invalid JSON: {exc}

What it means

After loading the plan string (inline or from file), the sanitizer calls json.loads(plan_str). On json.JSONDecodeError it writes `[CompetitorsPlan] Invalid JSON: <exc>` and raises SystemExit(2). The captured exception carries the line/column, which is shown to the user.

Source

Thrown at design-templates/last30days/scripts/last30days.py:324

    Accepts inline JSON or a file path (matches --plan). Returns {} on None/empty.
    Validation: top-level must be a dict; each value must be a dict. Unknown fields
    in entry values log a warning but do not abort. Invalid JSON or non-dict shape
    raises SystemExit(2) with a clear stderr message.
    """
    if not raw:
        return {}
    plan_str = raw
    if os.path.isfile(plan_str):
        try:
            plan_str = open(plan_str).read()
        except OSError as exc:
            sys.stderr.write(f"[CompetitorsPlan] Cannot read plan file: {exc}\n")
            raise SystemExit(2)
    try:
        parsed = json.loads(plan_str)
    except json.JSONDecodeError as exc:
        sys.stderr.write(f"[CompetitorsPlan] Invalid JSON: {exc}\n")
        raise SystemExit(2)
    if not isinstance(parsed, dict):
        sys.stderr.write(
            f"[CompetitorsPlan] Top-level must be a dict of "
            f"{{entity: {{targeting}}}}, got {type(parsed).__name__}\n"
        )
        raise SystemExit(2)
    known_fields = {
        "x_handle", "x_related", "subreddits",
        "github_user", "github_repos", "context",
    }
    normalized: dict[str, dict] = {}
    for entity, entry in parsed.items():
        if not isinstance(entry, dict):
            sys.stderr.write(
                f"[CompetitorsPlan] Entry for {entity!r} must be a dict, "
                f"got {type(entry).__name__}; skipping.\n"
            )
            continue

View on GitHub (pinned to 5be4028344)

Solutions

  1. Validate the JSON independently: `python3 -m json.tool <path>` or `jq . <path>`.
  2. Fix the reported line/column from the interpolated exception.
  3. Use double quotes for all keys and string values; remove trailing commas.
  4. Ensure the file is pure JSON (no leading YAML front-matter, no Markdown fences).

Example fix

# before
--plan '{"acme": {x_handle: "acme",},}'
# after
--plan '{"acme": {"x_handle": "acme"}}'
Defensive patterns

Strategy: validation

Validate before calling

import json
try:
    parsed = json.loads(plan_str)
except json.JSONDecodeError as exc:
    raise ValueError(f'plan is not valid JSON: {exc}') from exc

Prevention

When it happens

Trigger: `--plan '{"acme":'` (truncated), trailing comma, single quotes used instead of double quotes, a control character, or a file whose content is YAML/Markdown rather than JSON.

Common situations: Hand-writing JSON and forgetting a brace; copy-paste that dropped the closing bracket; passing a YAML plan by mistake; BOM or smart-quotes from a word processor.

Understand the failure class

Related errors


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