xai-org/grok-build · error · CompactionSampleError::Other

No turns remaining after filtering

Error message

No turns remaining after filtering

What it means

Raised by `sample_compaction_chunked` in the inter-compaction pipeline after filtering turns (e.g. removing oversized or non-compactable items) leaves an empty set. Compaction cannot proceed with zero turns, so the sampler bails with a CompactionSampleError::Other instead of producing a meaningless summary. It is an input-validation guard, not an LLM failure.

Source

Thrown at crates/common/xai-grok-compaction/src/inter_compaction/compact.rs:125

        strategy = strategy_label,
        num_turns = turns.len(),
        chunk_token_limit,
        user_compact_threshold = config.user_message_compact_threshold,
        "[InterCompaction] starting chunked compaction"
    );

    // Step 1 — filter.
    let filtered = filter_turns_for_inter_compaction(turns);
    info!(
        conversation_id = %conversation_id,
        start_response_id = %start_response_id,
        last_response_id = %response_id,
        original = turns.len(),
        filtered = filtered.len(),
        "[InterCompaction] filtered turns"
    );
    if filtered.is_empty() {
        return Err(CompactionSampleError::Other(anyhow::anyhow!(
            "No turns remaining after filtering"
        )));
    }

    // Step 2 — split prior `<grok_user_queries>` out of every prior
    // compaction summary item. The LLM never sees them (it would re-emit
    // them verbatim and snowball across rounds); they are reattached to
    // the final summary via `assemble_user_queries_preamble`. Shared with
    // intra-compaction's `History` target.
    let separated = separate_prior_user_queries(&filtered);

    // Step 3 — chunk + flush over the LLM-safe item list.
    let mut compactable: Vec<T> = Vec::new();
    let mut chunk_tokens: u32 = 0;
    let mut chunk_outputs: Vec<LlmCompactionOutput> = Vec::new();
    let mut chunk_idx: usize = 0;

    for turn in &separated.turns_for_llm {

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Check the input turn list before calling; skip compaction entirely when turns.len() == 0 or when all turns would be filtered out.
  2. Log/inspect the `filtered` count in the adjacent tracing line to see which filter predicate removed all turns.
  3. Relax the filter predicate or fall back to unfiltered sampling when filtering yields an empty set.
  4. Return early with a no-op result instead of invoking compaction for trivially small histories.

Example fix

// before
if filtered.is_empty() {
    return Err(CompactionSampleError::Other(anyhow::anyhow!("No turns remaining after filtering")));
}
// after
if filtered.is_empty() {
    if turns.is_empty() {
        return Ok(CompactionSample::nothingToDo());
    }
    tracing::warn!(original = turns.len(), "filter removed all turns; using unfiltered");
    let filtered = turns.clone();
}
Defensive patterns

Strategy: validation

Validate before calling

fn compactable(turns: &[Turn], pred: impl Fn(&Turn) -> bool) -> bool {
    turns.iter().any(pred) // at least one turn survives the filter
}
if !compactable(&turns, &filter_pred) { skip_compaction(); }

Try / catch

match sample_compaction_chunked(&turns).await {
    Ok(sample) => sample,
    Err(CompactionSampleError::Other(e))
        if e.to_string().contains("No turns remaining") => CompactionSample::nothingToDo(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling sample_compaction_chunked with a turn list that, after the filter step, contains no entries — e.g. all turns were dropped by the filter predicate, or the input list was empty to begin with.

Common situations: Compacting a brand-new session with no eligible turns; a filter whose thresholds (turn size limits, tool-output exclusion) reject everything after a schema change; passing an empty/already-compacted history.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/fe3923bc6c0d47ca. Report an issue: GitHub.