huggingface/candle · error

seqlens_k must be a cuda tensor

Error message

seqlens_k must be a cuda tensor

What it means

Raised during flash-attn v2 forward with variable-length sequences in candle-flash-attn/src/lib.rs when `seqlens_k` is provided but its storage is not CUDA memory. seqlens_k (per-batch key sequence lengths as u32/i32) must live on the CUDA device so it can be passed as a device slice to the kernel.

Source

Thrown at candle-flash-attn/src/lib.rs:494

        // https://github.com/Dao-AILab/flash-attention/blob/184b992dcb2a0890adaa19eb9b541c3e4f9d2a08/csrc/flash_attn/flash_api.cpp#L327
        let dev = q.device();
        let out_shape = q_l.shape().clone();
        let out_l = Layout::contiguous(&out_shape);

        let (seqlens_q, seqlens_q_layout) = self.seqlens_q.storage_and_layout();
        let seqlens_q = match &*seqlens_q {
            candle::Storage::Cuda(c) => c.as_cuda_slice::<u32>()?, // Should be i32!
            _ => candle::bail!("seqlens_q must be a cuda tensor"),
        };
        let seqlens_q = match seqlens_q_layout.contiguous_offsets() {
            Some((o1, o2)) => seqlens_q.slice(o1..o2),
            None => candle::bail!("seqlens_q has to be contiguous"),
        };

        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,

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Call .to_device(&dev) on seqlens_k before the call
  2. Create both seqlens tensors on the same CUDA device as q/k/v
  3. Add a device equality assert for all kernel inputs in your model forward

Example fix

// before
let seqlens_k = Tensor::from_vec(kv_lens, (batch + 1,), &Device::Cpu)?;
// after
let seqlens_k = Tensor::from_vec(kv_lens, (batch + 1,), &Device::Cpu)?.to_device(&dev)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_u32_cuda_k(t: &Tensor) -> candle::Result<Tensor> {
    if t.dtype() != candle::DType::U32 { candle::bail!("seqlens_k must be u32"); }
    if t.device().is_cuda() { Ok(t.clone()) } else { t.to_device(&Device::new_cuda(0)?) }
}

Type guard

fn seqlens_k_ready(t: &Tensor) -> bool { t.dtype() == candle::DType::U32 && t.device().is_cuda() }

Try / catch

let seqlens_k = seqlens_k.to_device(q.device())?;
match flash_attn_varlen(&q, &k, &v, &seqlens_q, &seqlens_k, scale, max_q, max_k, None, None, None) {
    Err(e) if e.to_string().contains("seqlens_k must be a cuda tensor") => { /* fix device, retry */ }
    r => r?,
}

Prevention

When it happens

Trigger: Calling flash_attn_varlen with seqlens_k created on/left on the CPU device while other inputs are on CUDA.

Common situations: Moving only q/k/v/seqlens_q to GPU and forgetting seqlens_k; constructing k-side batch metadata separately on CPU.

Related errors


AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02). Data as JSON: /api/errors/9044e120cdf7a639. Report an issue: GitHub.