HKUDS/Vibe-Trading · error · ValueError

label_end_times is empty

Error message

label_end_times is empty

What it means

_as_label_spans converts label end times into per-observation span end positions for purged cross-validation. When given a pandas Series, an empty Series means there are no observations/labels to build spans from, so the function refuses immediately rather than producing empty, ambiguous output.

Source

Thrown at agent/src/quantlib/crossvalidation.py:133

    Args:
        label_end_times: Either a pandas Series whose index is the label start
            time and whose values are the label end time, or a positional array
            where element ``i`` is the last positional index observation ``i``'s
            label depends on.
        n_samples: Expected number of samples, checked when supplied.

    Returns:
        Integer array ``ends`` where ``ends[i]`` is the last positional index
        that observation ``i``'s label covers. Always at least ``i``.

    Raises:
        ValueError: If the input is empty, not 1-D, holds a non-finite value, or
            declares a label ending before it starts.
    """
    if isinstance(label_end_times, pd.Series):
        if label_end_times.empty:
            raise ValueError("label_end_times is empty")
        starts = label_end_times.index
        ends = label_end_times.to_numpy()
        # searchsorted on the start index converts label end *times* into label
        # end *positions*; the right insertion point minus one keeps a label
        # that ends between two observations attached to the earlier one.
        positions = np.searchsorted(starts, ends, side="right") - 1
        positions = np.clip(positions, np.arange(len(starts)), len(starts) - 1)
        span_ends = positions.astype(int)
    else:
        span_ends = np.asarray(label_end_times, dtype=float)
        if span_ends.ndim != 1:
            raise ValueError(f"label_end_times must be 1-D, got shape {span_ends.shape}")
        if span_ends.size == 0:
            raise ValueError("label_end_times is empty")
        if not np.isfinite(span_ends).all():
            raise ValueError("label_end_times holds a non-finite value")
        span_ends = span_ends.astype(int)
        if (span_ends < np.arange(span_ends.size)).any():

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Check `len(label_end_times) > 0` before calling the split functions
  2. Fix the upstream filter/merge that emptied your dataset
  3. Skip empty folds/windows explicitly in your CV loop

Example fix

# before
folds = purged_kfold_splits(pd.Series(dtype=float), n_splits=5)

# after
ends = pd.Series([...non-empty...], index=X.index)
folds = purged_kfold_splits(ends, n_splits=5)
Defensive patterns

Strategy: validation

Validate before calling

if not isinstance(label_end_times, pd.Series) or label_end_times.empty:
    raise ValueError('label_end_times must be a non-empty pd.Series')
folds = purged_kfold_splits(label_end_times, n_splits=5)

Type guard

def is_non_empty_series(x) -> bool:
    return isinstance(x, pd.Series) and not x.empty and x.notna().all()

Try / catch

try:
    folds = purged_kfold_splits(ends, n_splits=5)
except ValueError as e:
    if 'empty' in str(e):
        logger.warning('empty fold window skipped')
        folds = []
    else:
        raise

Prevention

When it happens

Trigger: Calling purged_kfold_splits, purged_walk_forward_splits, combinatorial_purged_splits, or detect_boundary_leakage with label_end_times = pd.Series(dtype=object) or an empty pd.Series.

Common situations: Empty feature frames after filtering by date or ticker; upstream groupby producing an empty group; loading an empty CSV slice in a walk-forward pipeline.

Related errors


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