Troubleshooting
This page focuses on the current failure modes that actually show up in TinyLoop workflows.
Build Problems
Failed to find nvcc
Meaning:
- the active environment cannot find the CUDA toolkit
Check:
nvccexists- CUDA binaries are on
PATH CUDAToolkit_ROOTis correct
Python module did not build
Meaning:
- either
pybind11was not found - or
Python3 Development.Modulewas missing
Check the CMake configure output for:
pybind11_FOUNDPython3_Development.Module_FOUND
Model Load Problems
Invalid magic or unsupported version
Meaning:
- the file is not a valid
.tinyloopartifact - or the artifact version is outside the runtime's supported range
First action:
- run
inspect - confirm the converter wrote the expected version
dim must be divisible by n_heads
Meaning:
- the artifact header is inconsistent or corrupted
Check:
- source checkpoint config
- converter logic
- written header fields
Unsupported n_pre_blocks
Meaning:
- the current runtime assumes the narrow looped-transformer family it was built for
This is not a one-line patch. It is a deliberate runtime-extension task.
Runtime Problems
CUDA allocation failure
Common causes:
max_seq_lentoo large- model too large for the device
- FP16 body cache mode enabled on a too-small GPU
Try:
- smaller
max_seq_len inspectbefore loading- lower-memory runtime mode
- disable
TINYLOOP_EXPERIMENTAL_FP16_BODY=1
Generation output looks wrong
First isolate the source:
- compare cached vs uncached generation with
TINYLOOP_DISABLE_KV_CACHE=1 - compare new prefill path vs reference with
TINYLOOP_DISABLE_FLASH2_PREFILL=1 - if you are using the CLI, remember it tokenizes prompt text as raw bytes
If the Python/tokenizer-backed path looks right and the CLI does not, the problem may be text preprocessing rather than the runtime.
Benchmark numbers look inconsistent
Check:
- whether the run is cold or warmed
- whether
TINYLOOP_CUDA_PROFILE=1changed warmup behavior - whether
TINYLOOP_EXPERIMENTAL_FP16_BODY=1was enabled - whether another process was using the same GPU
KV cache mode env vars appear ignored
Symptom: you set TINYLOOP_KV_H_MODE=1 (or TINYLOOP_KV_INT8 / TINYLOOP_KV_INT4) but the decode latency and VRAM match the baseline FP16 KV path.
Cause: the env vars are probed on the first cache-allocation call and cached in a module-level static. If they were set after the first Model construction or the first generate call in the same process, they have no effect.
Fix:
- Set all
TINYLOOP_KV_*env vars beforeimport tinyloop_pyin Python, or beforeload_model()in C++. - For in-process mode switching (benchmarking multiple modes), use subprocess isolation — see Python API → KV cache modes for the template.
INT4-h falls back to FP16-h with a warning
Symptom: load log prints TINYLOOP_KV_INT4 requires head_dim to be even.
Cause: dim / n_heads is odd. INT4-h packs two nibbles per byte, so head_dim must be even.
Fix: use a model with an even head_dim. All reference TinyLoop checkpoints (407M, 1B-effective) satisfy head_dim = 128 and are unaffected.
INT4-h argmax differs from FP16-h on specific prompts
Symptom: greedy-decoded tokens diverge between FP16-h and INT4-h on some prompts.
Cause: INT4-h introduces ~10 % L2-relative K/V reconstruction error. On close-call logits (two candidates within ~0.1 in FP16), the reconstruction noise can flip the argmax. This is expected behaviour, not a bug — the error envelope is documented in the KV cache modes page.
Mitigation:
- Use
INT8 hinstead (~0.6 % L2-rel, argmax flips are rare). - Use
FP16 hfor quality-sensitive workloads. - If you have measured quality loss you consider unacceptable, please file an issue with the prompt + tokens.
Warm-start decode disagrees with fresh generate
Symptom: resume_generate(build_resume_handle(prompt, L_used=k), new_L=L) produces different tokens than generate(prompt, loops=L).
Under greedy decoding (temperature=0.0) this is a bug — the two paths should be bit-exact. Report it.
Under non-greedy sampling (temperature>0) this is expected: the warm-start path does not share the sampling PRNG state with the fresh-generate path. Nonzero-temperature parity is an open follow-up.
Prefix cache loops mismatch
Symptom: RuntimeError: loops mismatch on generate_from_prefix_cache.
Cause: the loops kwarg on generate_from_prefix_cache differs from the loops value used at build_prefix_cache time. A prefix is depth-specialized — you cannot reuse an L=8 prefix to serve an L=16 decode.
Fix:
- Match the kwargs. Use the same
loopson both calls. - If you need multiple depths on the same prompt, use warm-start mid-loop instead, or maintain one
PrefixCacheper depth.
OOM during long prefill with TINYLOOP_EXPERIMENTAL_FP16_BODY=1
Symptom: cudaMalloc failure during model load or early generation; GPU has enough memory for the model's INT4 size but not for the FP16 cache.
Cause: the FP16 body cache triples weight VRAM (1B-effective: 222 → 633 MB).
Mitigation options, in order of preference:
- Leave
TINYLOOP_EXPERIMENTAL_FP16_BODYoff and accept slower prefill. - Enable the combination
TINYLOOP_KV_H_MODE=1 TINYLOOP_KV_INT4=1 TINYLOOP_EXPERIMENTAL_FP16_BODY=1— the INT4-h KV savings offset the FP16 body cost for long-context workloads. - Reduce
max_seq_lenso runtime buffers + KV cache are smaller.
head_dim > 128 in alloc warning
The INT8 / INT4 pack/unpack kernels are templated on MAX_HEAD_DIM=128. If your model has head_dim > 128, the kernels silently return without packing.
Fix: bump the template bound in cuda/kv_cache.cu, rebuild, confirm the test harness still passes.
Test Problems
Model-dependent tests are skipping
Set:
TINYLOOP_TEST_MODEL_PATH=/absolute/path/to/model.tinyloop
Tokenizer-aware tests are skipping
Check:
- the Python binding was built
numpyis installedtransformersis installed
Python regression cannot import tinyloop_py
Set:
TINYLOOP_TEST_BUILD_DIR=/path/to/tinyloop/build
or add the build directory to PYTHONPATH.
When To Stop Guessing
If a bug is not obvious:
- reproduce it with a small command
- compare against the closest reference path
- preserve the exact env flags used
- run the smallest relevant test or microbenchmark
TinyLoop is moving fast enough that vague debugging usually wastes time.