huggingface/candle · error
seqlens_k must be a cuda tensor
Error message
seqlens_k must be a cuda tensor
What it means
The varlen kernel reads cu_seqlens_k (cumulative key-sequence offsets) directly on the GPU, so seqlens_k must live in candle::Storage::Cuda. storage_and_layout() returned non-CUDA storage, meaning the tensor is on CPU or another backend. The source also notes the slice is read as u32 though i32 offsets are the natural dtype.
Source
Thrown at candle-flash-attn-v3/src/lib.rs:474
// https://github.com/Dao-AILab/flash-attention/blob/0dfb28174333d9eefb7c1dd4292690a8458d1e89/hopper/flash_api.cpp
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 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();
let k_stride = k_l.stride();
let v_stride = v_l.stride();
let o_stride = out_l.stride();
View on GitHub (pinned to d5fee525bf)
Solutions
- Move seqlens_k to the same CUDA device as q: seqlens_k.to_device(q.device())?.
- Create cu_seqlens_k directly on the CUDA device via Tensor::from_vec(vec, (n + 1,), device).
- Add a device-equality assertion for all forward inputs at the attention module boundary.
Example fix
// before let seqlens_k = Tensor::from_vec(k_offsets, (n + 1,), &Device::Cpu)?; // after let seqlens_k = Tensor::from_vec(k_offsets, (n + 1,), q.device())?;
Defensive patterns
Strategy: validation
Validate before calling
if seqlens_k.device().is_cpu() {
let seqlens_k = seqlens_k.to_device(q.device())?;
}
debug_assert!(!seqlens_k.device().is_cpu(), "seqlens_k must be on the CUDA device"); Type guard
fn is_on_cuda(t: &candle_core::Tensor) -> bool { !t.device().is_cpu() } Try / catch
match forward(&q, &k, &v, &seqlens_q, &seqlens_k, ...) {
Err(e) if e.to_string().contains("seqlens_k must be a cuda tensor") => {
let sk = seqlens_k.to_device(q.device())?;
forward(&q, &k, &v, &seqlens_q, &sk, ...)
}
other => other,
} Prevention
- Create seqlens tensors on the same device as q/k/v from the start (pass q.device() into Tensor::from_vec).
- When moving a model to CUDA, move metadata tensors (cu_seqlens) too, not just weights.
- Assert device equality for all forward inputs at the module boundary.
When it happens
Trigger: Calling the varlen forward with seqlens_k still on the CPU device or on a different device than q/k/v — e.g. created with Tensor::from_vec on Device::Cpu and never moved.
Common situations: Moving model weights to CUDA but forgetting metadata tensors; q/k/v moved via .to_device while seqlens were left behind; constructing offsets before the target device is known.
Related errors
- alibi_slopes must be a cuda tensor
- seqlens_q must be a cuda tensor
- seqlens_k must be a cuda tensor
- block_table must be a cuda tensor
- Invalid quantize storage locations do not match
AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02).
Data as JSON: /api/errors/036ccc409ccd2e22.
Report an issue: GitHub.