huggingface/transformers · critical · ValueError
No real solution (discriminant = {})
Error message
No real solution (discriminant = {}) What it means
Raised by PagedAttentionCache._solve_quadratic when the discriminant of the memory-polynomial quadratic is negative, i.e. the equation a·x²+b·x+c=0 has no real root. In practice this means the memory constraint curve never crosses zero, so no batch size can satisfy it — the budget is mathematically unreachable for the given coefficients (e.g. constant memory cost alone exceeds available memory while the quadratic terms grow).
Source
Thrown at src/transformers/generation/continuous_batching/cache.py:747
def _check_footprint(self, max_batch_tokens: int, num_blocks: int) -> tuple[int, int]:
"""Checks if the footprint of the cache is within the available memory."""
memory_footprint = self.compute_memory_footprint(max_batch_tokens, num_blocks)
if memory_footprint > self.available_memory:
raise MemoryError(
f"Memory footprint {memory_footprint} is more than available memory {self.available_memory}"
)
if max_batch_tokens <= 0 or num_blocks <= 0:
raise ValueError(f"Invalid values: max_batch_tokens = {max_batch_tokens}, num_blocks = {num_blocks}")
return max_batch_tokens, num_blocks
def _solve_quadratic(self, a: float, b: float, c: float) -> int:
"""Largest positive root of a·x² + b·x + c = 0. Falls back to linear when a == 0. Rounded down."""
if a == 0:
return int(-c / b)
discriminant = b**2 - 4 * a * c
if discriminant < 0:
raise ValueError(f"No real solution (discriminant = {discriminant})")
root = (-b + sqrt(discriminant)) / (2 * a)
if root < 0:
raise ValueError(f"No positive solution (root = {root})")
return int(floor(root))
# Formatting is disabled because of comment indentation, which improves readability.
# fmt: off
def _equation_coefficients(self, peak_deltas: tuple[int, ...]) -> tuple[int, ...]:
"""Given some deltas corresponding to an activation peak, returns the coefficients for the memory polynomial of
that peak. The memory polynomial is described in that class docstring."""
delta_m, delta_n, delta_mm, delta_mn = peak_deltas
i = torch.int32.itemsize # size of int32 in bytes, used for index, input_ids, ...
a = self.activation_dtype.itemsize # for now, the cache and the activation have the same dtype
c = self.cache_dtype.itemsize
k = self.io_multiplier # 1 sync, 2 async (IO tensors only)
# -- N terms: cost per cache page --------------------------------------------------View on GitHub (pinned to a597f97485)
Solutions
- Reduce num_blocks (if you set it explicitly) so the fixed cache cost fits available memory
- Free memory / use a smaller or quantized model to raise available_memory
- Use a smaller cache dtype to shrink the N-dependent coefficient
- Let the cache auto-size by passing None for both values with a sane cache_fill_per_batch instead of forcing one variable
Example fix
# before cfg = ContinuousBatchingConfig(num_blocks=4096) # fixed KV cost alone > available_memory # after cfg = ContinuousBatchingConfig(num_blocks=512)
Defensive patterns
Strategy: validation
Validate before calling
import torch
free_b, _ = torch.cuda.mem_get_info()
# rough KV bytes per block: 2 (k,v) * layers * block_size * kv_heads * head_dim * dtype_size
kv_per_block = 2 * num_layers * block_size * num_kv_heads * head_dim * 2
if cfg.num_blocks is not None and cfg.num_blocks * kv_per_block > free_b:
cfg.num_blocks = int(free_b * 0.8 // kv_per_block) Try / catch
try:
manager = model.continuous_batching(config=cfg)
except ValueError as e:
if 'No real solution' in str(e) and cfg.num_blocks:
cfg.num_blocks //= 2
manager = model.continuous_batching(config=cfg)
else:
raise Prevention
- Estimate KV bytes per block before setting num_blocks
- Leave 10-20% headroom on available memory
- Prefer auto-sizing (both values None) on unknown hardware
When it happens
Trigger: Solving for max_batch_tokens given num_blocks (N given branch) or auto-sizing both via cache_fill_per_batch, when available_memory is smaller than the constant term of the polynomial (cn·N - avail makes c so negative/positive that no real root exists).
Common situations: Very small GPU or near-zero available_memory; num_blocks set so large that even zero max_batch_tokens overshoots memory; incorrect activation_peak coefficients for an exotic architecture.
Related errors
- No positive solution (root = {})
- Invalid values: max_batch_tokens = {}, num_blocks = {}
- Failed to allocate {} blocks for request {}
- Memory footprint {} is more than available memory {}
- No requests can be scheduled and no requests can be offloade
AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14).
Data as JSON: /api/errors/1390bded0bc93c7a.
Report an issue: GitHub.