rohitg00/ai-engineering-from-scratch · error · ValueError

malformed line

Error message

malformed line: {line!r}

What it means

_parse_mapping is a hand-rolled YAML subset parser that accepts 'key: value' lines at a consistent indent; any line that is not a list item or a well-formed key-value pair at the expected indent falls through to the generic guard, meaning the YAML uses syntax outside the supported subset (flow maps, anchors, tags, or stray text).

Solutions

  1. Rewrite the offending line as plain 'key: value' block syntax
  2. Avoid flow style ({...}), anchors (&, *), and multi-document YAML in constitutions processed by this parser
  3. Switch the loader to a full YAML library if the document legitimately needs richer syntax
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at phases/19-capstone-projects/86-constitutional-rules-engine/code/yaml_subset.py:120 when the library encounters an invalid state.

Common situations: See trigger scenarios.

Understand the failure class


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/130022c7fa4f9d81. Report an issue: GitHub.

Appendix: source

Thrown at phases/19-capstone-projects/86-constitutional-rules-engine/code/yaml_subset.py:120


def _parse_mapping(lines: list[str], start: int, indent: int) -> tuple[dict[str, Any], int]:
    out: dict[str, Any] = {}
    i = start
    while i < len(lines):
        line = lines[i]
        cur_indent = _indent_of(line)
        if cur_indent < indent:
            break
        if cur_indent > indent:
            i += 1
            continue
        stripped = line.lstrip()
        if stripped.startswith("- "):
            break
        m = re.match(r"^([A-Za-z_][\w-]*)\s*:\s*(.*)$", stripped)
        if not m:
            raise ValueError(f"malformed line: {line!r}")
        key = m.group(1)
        rest = m.group(2)
        if rest.strip() == "":
            if i + 1 < len(lines) and _indent_of(lines[i + 1]) > indent:
                value, i = _parse_block(lines, i + 1, _indent_of(lines[i + 1]))
                out[key] = value
                continue
            out[key] = None
            i += 1
        else:
            out[key] = _coerce(rest)
            i += 1
    return out, i


def _parse_sequence(lines: list[str], start: int, indent: int) -> tuple[list[Any], int]:
    out: list[Any] = []
    i = start

View on GitHub (pinned to 39ea8a1c6d)