affaan-m/ECC · error · ContractError
genre numbers must be distinct
Error message
genre numbers must be distinct
What it means
validate_genre_specs requires each genre spec to have a unique 'number'. Duplicate numbers would make downstream per-genre outputs ambiguous (two specs targeting the same numbered slot), so duplicates are rejected.
Solutions
- Renumber the specs so each has a distinct 'number': 1, 2, 3, ...
- Deduplicate specs that are true copies rather than renumbering them
- Renumber programmatically: [dict(s, number=i+1) for i, s in enumerate(specs)] before validation
- Fix the merging logic that produced duplicate numbers
Example fix
# before
specs = [{"number": 1, ...}, {"number": 1, ...}]
# after
specs = [{"number": 1, ...}, {"number": 2, ...}] Defensive patterns
Strategy: validation
Validate before calling
numbers = [s.get("number") for s in specs]
if len(numbers) != len(set(numbers)):
raise SystemExit(f'duplicate genre numbers: {[n for n in numbers if numbers.count(n) > 1]}') Type guard
def has_unique_numbers(specs) -> bool:
numbers = [s.get("number") for s in specs]
return len(numbers) == len(set(numbers)) Try / catch
try:
validate_bundle(root)
except ContractError as e:
if 'distinct' in str(e) and 'numbers' in str(e):
print('Renumber genre specs so each number is unique')
else:
raise Prevention
- Renumber programmatically after merging spec files: enumerate and reassign
- Never copy-paste spec blocks without bumping the number
- Run the uniqueness check in config linters before validation
When it happens
Trigger: Two or more genre specs share the same 'number' value — e.g. copy-pasting a spec block and forgetting to bump its number, or programmatically appending specs that default to the same number.
Common situations: Editing a multi-genre config by duplicating an existing entry, template configs where numbers were placeholders (all 1), or merging two spec files without renumbering.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- application config exceeds local size limit
- Asset request_id/modality does not match the bundle
- at least one genre spec is required
- bundle collections must be lists
- candidate configuration exceeds 1 MiB
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/b443f8249de9c755.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:123
while pending:
directory = pending.pop()
with os.scandir(directory) as entries:
for entry in entries:
if entry.is_symlink():
raise ContractError(f"output bundle contains a symlink: {entry.path}")
if entry.is_dir(follow_symlinks=False):
pending.append(Path(entry.path))
elif not entry.is_file(follow_symlinks=False):
raise ContractError(f"output bundle contains a special file: {entry.path}")
def validate_genre_specs(specs: list[dict[str, Any]]) -> None:
"""Require complete, semantically distinct numbered genre specs."""
if not specs:
raise ContractError("at least one genre spec is required")
numbers = [spec.get("number") for spec in specs]
if len(numbers) != len(set(numbers)):
raise ContractError("genre numbers must be distinct")
fingerprints = [spec.get("style_fingerprint") for spec in specs]
signatures = [_semantic_signature(spec) for spec in specs]
if len(fingerprints) != len(set(fingerprints)) or len(signatures) != len(set(signatures)):
raise ContractError("genre references collapsed into a generic style; distinct specs required")
for spec in specs:
if spec.get("dry_run") is not True:
raise ContractError(f"genre {spec.get('number')} crosses the dry-run boundary")
measured = spec.get("measured_features")
if measured is not None:
if not isinstance(measured, dict):
raise ContractError(f"genre {spec.get('number')} has invalid measured evidence")
_validate_numeric_evidence(measured, label=f"genre {spec.get('number')} evidence")
total_duration = measured.get("total_duration")
if not _is_finite_real(total_duration):
raise ContractError(f"genre {spec.get('number')} has invalid total duration")
total_duration = cast(float, total_duration)View on GitHub (pinned to 8321021c54)