HKUDS/Vibe-Trading · error · ValueError

payload must not set reserved chain fields: {sorted(reserved

Error message

payload must not set reserved chain fields: {sorted(reserved_used)}

What it means

append_record refuses payloads containing chain-managed fields (seq, prev_hash, hash, etc., i.e. _CHAIN_FIELDS). These are computed by the ledger itself; letting callers set them would break the hash chain's integrity model.

Source

Thrown at agent/src/governance/ledger.py:410

            directory the first time the file is created (default ``True``,
            matching ``TraceWriter``'s per-record durability guarantee — see
            module docstring for why the compliance ledger did not have this
            before). ``False`` issues no fsync syscalls at all.
        dir_mode: Permission bits for the parent directory when created.

    Returns:
        The full record as written: ``payload`` merged with ``seq``,
        ``prev_record_hash``, ``record_hash``.

    Raises:
        ValueError: ``payload`` sets a reserved chain-field key.
        LedgerCorruptionError: The ledger's existing chain is already broken
            (a prior record was edited or deleted) — the append is refused
            rather than silently extended on top of unknown history.
    """
    reserved_used = _CHAIN_FIELDS & payload.keys()
    if reserved_used:
        raise ValueError(f"payload must not set reserved chain fields: {sorted(reserved_used)}")

    path.parent.mkdir(parents=True, exist_ok=True, mode=dir_mode)
    created = not path.exists()

    handle = open(path, "a+b")
    try:
        _lock_exclusive(handle)
        try:
            handle.seek(0)
            existing_text = handle.read().decode("utf-8")
            # Sealed segments come first: after rotate_if_needed the active
            # file starts partway through the chain, and walking it as if it
            # began at genesis would report every rotated ledger as corrupt.
            # Continuing from the newest segment is also what makes deleting a
            # whole segment leave a detectable seam.
            start_seq, start_prev = 1, GENESIS_PREV_HASH
            segments = archive_segments(path)
            if segments:

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Strip reserved keys before appending: {k: v for k, v in payload.items() if k not in _CHAIN_FIELDS}
  2. Re-read the ledger API docs — chain fields are outputs, never inputs
  3. Add a helper that sanitizes replayed records

Example fix

# before
append_record(path, payload=dict(old_record))  # old_record has seq/prev_hash/hash
# after
reserved = {"seq", "prev_hash", "hash", "record_hash"}
append_record(path, payload={k: v for k, v in old_record.items() if k not in reserved})
Defensive patterns

Strategy: validation

Validate before calling

payload = {k: v for k, v in payload.items() if k not in _CHAIN_FIELDS}

Type guard

def is_appendable_payload(payload: dict, chain_fields: set) -> bool:
    return not (chain_fields & payload.keys())

Try / catch

except ValueError as e: if 'reserved chain fields' in str(e): strip the listed keys and retry append_record

Prevention

When it happens

Trigger: Calling append_record(path, payload={..., "seq": 6}) or including prev_hash/hash/record_hash keys in the payload dict.

Common situations: Round-tripping a read record straight back into append_record without stripping chain fields; building payloads from templates that include chain metadata.

Related errors


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