huggingface/candle · error
block_table last dimension must be contiguous
Error message
block_table last dimension must be contiguous
What it means
Raised in candle-flash-attn/src/lib.rs when the `block_table` tensor's last (innermost) dimension stride is not 1, i.e. its rows are not contiguous. The kernel indexes the block table assuming a row-major contiguous layout, so call `.contiguous()` (or reshape/transpose appropriately) before passing it.
Source
Thrown at candle-flash-attn/src/lib.rs:509
let (seqlens_k, seqlens_k_layout) = self.seqlens_k.storage_and_layout();
let seqlens_k = match &*seqlens_k {
candle::Storage::Cuda(c) => c.as_cuda_slice::<u32>()?, // Should be i32!
_ => candle::bail!("seqlens_k must be a cuda tensor"),
};
let seqlens_k = match seqlens_k_layout.contiguous_offsets() {
Some((o1, o2)) => seqlens_k.slice(o1..o2),
None => candle::bail!("seqlens_k has to be contiguous"),
};
let block_table = if let Some(block_table) = self.block_table.as_ref() {
let (block_table_storage, block_table_layout) = block_table.storage_and_layout();
match &*block_table_storage {
candle::Storage::Cuda(_) => {}
_ => candle::bail!("block_table must be a cuda tensor"),
}
let block_table_stride = block_table_layout.shape().dims2()?.1;
if block_table_layout.stride().last().copied() != Some(1) {
candle::bail!("block_table last dimension must be contiguous")
}
Some((
block_table_storage,
block_table_layout.start_offset(),
block_table_stride,
))
} else {
None
};
let q = q.as_cuda_slice::<T>()?;
let k = k.as_cuda_slice::<T>()?;
let v = v.as_cuda_slice::<T>()?;
let q = q.slice(q_l.start_offset()..);
let k = k.slice(k_l.start_offset()..);
let v = v.slice(v_l.start_offset()..);
let q_stride = q_l.stride();View on GitHub (pinned to d5fee525bf)
Solutions
- Call .contiguous() on block_table before passing it
- Ensure it is created as a contiguous [batch, max_num_blocks_per_seq] tensor
- Avoid permute/transpose on the table; build it in the required layout
Example fix
// before let block_table = pages_buf.t()?.contiguous()?; // wrong layout then fix let block_table = pages_buf.t()?; // last dim strided // after let block_table = pages_buf.t()?.contiguous()?; // stride 1 on last dim
Defensive patterns
Strategy: validation
Validate before calling
fn ensure_block_table_layout(bt: &Tensor) -> candle::Result<Tensor> {
let (_, last_stride) = (bt.dim(0)?, bt.stride()[1]);
if last_stride != 1 { return bt.contiguous(); }
Ok(bt.clone())
}
let block_table = ensure_block_table_layout(&block_table)?; Type guard
fn block_table_contiguous(t: &Tensor) -> bool {
t.dims().len() == 2 && *t.stride().last().unwrap_or(&0) == 1
} Try / catch
let block_table = block_table.map(|bt| bt.contiguous()).transpose()?;
match flash_attn_varlen(&q, &k, &v, &sq, &sk, scale, max_q, max_k, None, None, block_table.as_ref()) {
Err(e) if e.to_string().contains("block_table last dimension must be contiguous") => { /* rebuild table contiguously */ }
r => r?,
} Prevention
- Never transpose/permute the block table; build it in [batch, max_blocks] order
- Call .contiguous() on tables derived from views or slices
- Assert last stride == 1 in debug builds before paged attention calls
When it happens
Trigger: Passing a block_table view whose last axis is strided — e.g. a transposed table, a column slice of a wider buffer, or any tensor from narrow/permute without contiguity in dim 1.
Common situations: Reusing a (max_blocks, batch) table transposed to (batch, max_blocks) without .contiguous(); slicing page tables out of a fused metadata tensor.
Related errors
- seqlens_q has to be contiguous
- seqlens_k has to be contiguous
- seqlens_q has to be contiguous
- seqlens_k has to be contiguous
- block_table must be a cuda tensor
AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02).
Data as JSON: /api/errors/151a90233dccadb9.
Report an issue: GitHub.