HKUDS/Vibe-Trading · error · ValueError
{n_groups} unique groups cannot make {n_folds} folds
Error message
{n_groups} unique groups cannot make {n_folds} folds What it means
group_purged_kfold_splits forms folds from whole groups, so it needs at least n_folds unique groups; each fold's test set is one or more groups. Fewer unique groups than folds makes the split impossible and is rejected.
Source
Thrown at agent/src/quantlib/crossvalidation.py:321
if n_folds < MIN_FOLDS:
raise ValueError(f"n_folds must be at least {MIN_FOLDS}, got {n_folds}")
if not 0.0 <= embargo_fraction < 1.0:
raise ValueError(f"embargo_fraction must be in [0, 1), got {embargo_fraction}")
grp_array = np.asarray(groups)
n_samples = len(grp_array)
if n_samples == 0:
raise ValueError("groups array cannot be empty")
# Find unique groups preserving chronological order of appearance
unique_groups, first_indices = np.unique(grp_array, return_index=True)
# Sort by appearance order
order = np.argsort(first_indices)
unique_groups = unique_groups[order]
n_groups = len(unique_groups)
if n_groups < n_folds:
raise ValueError(f"{n_groups} unique groups cannot make {n_folds} folds")
# Map each group to its member row indices
group_to_rows: dict[object, np.ndarray] = {}
for idx, g in enumerate(grp_array):
group_to_rows.setdefault(g, []).append(idx)
for g in group_to_rows:
group_to_rows[g] = np.array(group_to_rows[g], dtype=int)
embargo_groups = int(round(n_groups * embargo_fraction))
boundaries = np.linspace(0, n_groups, n_folds + 1).astype(int)
for fold in range(n_folds):
start_g, stop_g = int(boundaries[fold]), int(boundaries[fold + 1])
if stop_g <= start_g:
continue
test_groups = set(unique_groups[start_g:stop_g])
embargo_end_g = min(n_groups, stop_g + embargo_groups)View on GitHub (pinned to 80ffdda44c)
Solutions
- Lower n_folds to at most the number of unique groups
- Choose a higher-cardinality grouping (e.g. per-date instead of per-ticker) if more folds are needed
- Fix the group key if it is accidentally constant (wrong column, dtype mismatch after merge)
Example fix
// before splits = list(group_purged_kfold_splits(X, groups=df['asset_class'], n_folds=6)) # 4 unique classes // after n_groups = df['asset_class'].nunique() splits = list(group_purged_kfold_splits(X, groups=df['asset_class'], n_folds=min(6, n_groups)))
Defensive patterns
Strategy: validation
Validate before calling
n_groups = len(set(groups))
assert n_groups >= n_folds, f'{n_groups} groups cannot make {n_folds} folds' Prevention
- Use n_folds = min(cfg.n_folds, pd.Series(groups).nunique())
- Pick group keys with cardinality comfortably above the fold count
When it happens
Trigger: Calling with groups containing 3 unique values but n_folds=5, e.g. groups=[1,1,2,2,3,3] with n_folds=5, or a grouping column that is constant.
Common situations: Grouping by a low-cardinality column (weekday with weekends removed -> 5 values max), a constant/buggy group key, or raising n_folds for a panel with few entities.
Related errors
- groups array cannot be empty
- a label cannot end before the observation it belongs to star
- label_end_times has {span_ends.size} entries but the sample
- n_folds must be at least {MIN_FOLDS}, got {n_folds}
- {n_samples} samples cannot make {n_folds} folds
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/b90eea82e5543808.
Report an issue: GitHub.