nexu-io/open-design · error · ValueError
representative_ids must be a subset of candidate_ids
Error message
representative_ids must be a subset of candidate_ids
What it means
Cluster.__post_init__ enforces that representative_ids is a subset of candidate_ids. A cluster's 'representative' candidates must be drawn from its own candidate pool; an out-of-set representative would reference a candidate that is not part of the cluster, corrupting downstream rendering and evidence linking.
Source
Thrown at design-templates/last30days/scripts/lib/schema.py:139
cluster_id: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass
class Cluster:
"""Ranked cluster of related candidates."""
cluster_id: str
title: str
candidate_ids: list[str]
representative_ids: list[str]
sources: list[str]
score: float
uncertainty: Literal["single-source", "thin-evidence"] | None = None
def __post_init__(self) -> None:
if not set(self.representative_ids) <= set(self.candidate_ids):
raise ValueError("representative_ids must be a subset of candidate_ids")
@dataclass
class Report:
"""Final pipeline output."""
topic: str
range_from: str
range_to: str
generated_at: str
provider_runtime: ProviderRuntime
query_plan: QueryPlan
clusters: list[Cluster]
ranked_candidates: list[Candidate]
items_by_source: dict[str, list[SourceItem]]
errors_by_source: dict[str, str]
warnings: list[str] = field(default_factory=list)
artifacts: dict[str, Any] = field(default_factory=dict)View on GitHub (pinned to 5be4028344)
Solutions
- Build representative_ids only from ids present in candidate_ids (intersect first).
- If candidate_ids is filtered post-hoc, recompute representatives as candidate_ids ∩ old_representatives.
- In cluster merge, rebuild representatives from the merged candidate pool.
Example fix
# before Cluster(cluster_id='c', title='t', candidate_ids=['a','b'], representative_ids=['a','z'], sources=['x'], score=0.5) # after cands = ['a', 'b'] reps = [r for r in ['a', 'z'] if r in cands] or cands[:1] Cluster(cluster_id='c', title='t', candidate_ids=cands, representative_ids=reps, sources=['x'], score=0.5)
Defensive patterns
Strategy: validation
Validate before calling
def build_cluster(cluster_id, title, candidate_ids, representative_ids, sources, score):
cand_set = set(candidate_ids)
reps = [r for r in representative_ids if r in cand_set] or candidate_ids[:1]
return Cluster(cluster_id, title, candidate_ids, reps, sources, score) Type guard
def representatives_are_subset(candidate_ids, representative_ids) -> bool:
return set(representative_ids) <= set(candidate_ids) Try / catch
null
Prevention
- Always derive representative_ids from candidate_ids (intersect), never from an external list.
- When filtering candidate_ids, recompute representatives as old_reps ∩ new_candidates.
- Add a unit test that asserts representatives_are_subset for every cluster built by the clustering stage.
When it happens
Trigger: Constructing Cluster with representative_ids containing an id absent from candidate_ids. Reached when clustering code copies representative ids from a different cluster, or when candidate_ids was filtered after representatives were chosen.
Common situations: Clustering refactor that picks representatives before finalizing the candidate list. Deduplication step that removed a candidate but left its id in representatives. Cross-cluster merge that did not recompute representatives.
Related errors
- SubQuery must have at least one source
- SubQuery weight must be positive, got {self.weight}
- project is required (string).
- invalid JSON in ${filePath}: ${message}
- ${filePath} must contain a JSON object
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/df55985f8bd64273.
Report an issue: GitHub.