Hmbown/CodeWhale · error · RuntimeContractError

tool surface_profile must be ` `, got

Error message

{kind} tool surface_profile must be `{TOOL_SURFACE_PROFILE}`, got {profile!r}

What it means

validate_identity_structure pins the tool catalog to the script's canonical surface profile constant TOOL_SURFACE_PROFILE. If the document's tool_catalog.surface_profile differs, the contract identity is not comparable with the current script version, so it throws. This prevents mixing receipts from different catalog shapes.

Solutions

  1. Regenerate the receipt with the current script so surface_profile matches TOOL_SURFACE_PROFILE
  2. Read TOOL_SURFACE_PROFILE in check-runtime-contract-budget.py and correct the JSON value to that exact string
  3. If the profile change is intentional, update TOOL_SURFACE_PROFILE and regenerate all receipts consistently

Example fix

// before
{"tool_catalog": {"surface_profile": "codewhale-v1"}}
// after (value = TOOL_SURFACE_PROFILE in the script)
{"tool_catalog": {"surface_profile": "codewhale-v2"}}
Defensive patterns

Strategy: validation

Validate before calling

from scripts.check_runtime_contract_budget import TOOL_SURFACE_PROFILE
assert doc.get("tool_catalog", {}).get("surface_profile") == TOOL_SURFACE_PROFILE, \
    f"expected surface_profile {TOOL_SURFACE_PROFILE!r}"

Try / catch

try:
    validate_receipt(receipt)
except RuntimeContractError as e:
    if "surface_profile must be" in str(e):
        receipt = regenerate_receipt()  # version skew: rebuild with current script
    else:
        raise

Prevention

When it happens

Trigger: validate_receipt or validate_budget finds tool_catalog.surface_profile set to any value other than TOOL_SURFACE_PROFILE (or missing, which surfaces as error 800 first).

Common situations: Receipt generated by an older/newer script version where the profile string was bumped; manually crafted JSON with a guessed profile value; a document from another repo/copy with a divergent constant.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/ee4f160edbc2c961. Report an issue: GitHub.

Appendix: source

Thrown at scripts/check-runtime-contract-budget.py:208

def required_value(document: dict[str, Any], path: MetricPath, kind: str) -> Any:
    value: Any = document
    dotted = ".".join(path)
    for part in path:
        if not isinstance(value, dict) or part not in value:
            raise RuntimeContractError(f"{kind} is missing required field `{dotted}`")
        value = value[part]
    return value


def tool_identity_digest(names: list[str]) -> str:
    return hashlib.sha256("\0".join(names).encode("utf-8")).hexdigest()


def validate_identity_structure(document: dict[str, Any], kind: str) -> None:
    profile = required_value(document, ("tool_catalog", "surface_profile"), kind)
    if profile != TOOL_SURFACE_PROFILE:
        raise RuntimeContractError(
            f"{kind} tool surface_profile must be `{TOOL_SURFACE_PROFILE}`, "
            f"got {profile!r}"
        )

    shell = required_value(document, ("tool_catalog", "execution_shell"), kind)
    if shell != "bash":
        raise RuntimeContractError(
            f"{kind} tool execution_shell must be `bash`, got {shell!r}"
        )

    for mode, _label in VISIBLE_MODES:
        for surface, _surface_label in TOOL_SURFACES:
            base = ("tool_catalog", "modes", mode, surface)
            names = required_value(document, (*base, "tool_names"), kind)
            dotted_names = ".".join((*base, "tool_names"))
            if (
                not isinstance(names, list)
                or any(not isinstance(name, str) or not name for name in names)

View on GitHub (pinned to 433685b202)