Skip to main content
Most flash errors print one clean line; add the global --debug flag before the subcommand (e.g. flash --debug train config.toml) for the full traceback.

Installation & CLI

The CLI installs a single flash command. Its install location must be on your PATH.
  • If you installed with uv tool install freesolo-flash, make sure uv’s tool bin directory is on your PATH (run uv tool update-shell, then restart your shell).
  • Confirm the install with flash version.
The CLI is published to PyPI as freesolo-flash. The bare flash name belongs to an unrelated project. Reinstall the right one:

Authentication

Commands that contact Freesolo authenticate with a Freesolo API key, verified at login. flash train --cost never submits a training run, but on an SFT config it does authenticate.
  • Create a key in your dashboard at freesolo.co.
  • Log in once: flash login --api-key <your-key> (or set FREESOLO_API_KEY instead of passing --api-key).
  • Confirm who the stored key resolves to: flash whoami.
By default the CLI talks to https://api.freesolo.co. To target a different deployment, set --freesolo-url (or FREESOLO_BASE_URL) at login. See Auth & identity.

Environments

The folder you push must contain an environment.py file with a load_environment() function that returns a Freesolo environment. Then:
It prints the published id (your-org/math) to put in your config’s [environment] id. The folder needs an environment.py at its root, a --name whose namespace matches your org (or a bare name), and a --project UUID your org can reach - see Environments.
Managed training runs already have the Freesolo SDK. Your local Python environment does not get that SDK automatically from the flash CLI. Install it locally to run or test environment.py directly:
To pull a published env into your project for local work, use flash env pull your-org/your-env.
flash train --dry-run checks your config at submit time. On GRPO, OPD, and an already-profiled SFT config it does not run your environment, so it cannot prove every import resolves; only an uncached SFT dry-run loads it, by way of the profile run. If flash runs log <run-id> shows your environment.py failed while importing a package, that package is not available on the worker.The worker installs one managed set - the freesolo SDK and the training stack. To add to it, declare the package under [environment] pip, which is appended to the managed set rather than replacing it, then submit again. See Scorer dependencies for the accepted form.If you would rather not add a dependency, rewrite the import against the standard library - urllib.request and json instead of a vendor HTTP client, for example - or move that logic out of the environment, then republish. See Dependencies are managed.Flash also does not install from a pyproject.toml, requirements.txt, or lockfile bundled with the environment; those describe your local setup only.
Pull the specific file you need instead of the whole environment:
Keep published environments focused on source, small sidecars, and datasets needed by the run. Do not publish virtualenvs, local caches, model weights, or generated artifacts.
Use flash env pull to inspect the exact packaged file:
For clean A/B experiments, publish changed datasets under a fresh env name so old runs, new runs, and local files are easy to tell apart.
If your environment module shares a name with an installed Python package, it can shadow or be shadowed by that package. Keep helper module names distinct from installed packages.
The [environment] id must be a published Freesolo environment id, produced by flash env push, for example your-org/your-env. A local file path is not a valid id, so publish it first or reference an existing published id. Use flash env pull your-org/your-env only when you want a local copy to edit or inspect.

Configuration

model must be one of the ids in the curated catalog. List the valid ids:
Managed runs train catalog models only. See Supported models.
Flash rejects unknown config sections and [train] keys at parse time. Check the key against the configuration reference and validate the config:
Dry-run also flags [train] keys your CLI version does not recognize, which usually means an outdated CLI.
algorithm must be sft (the default), grpo, or opd. Fix the value and re-validate with --dry-run.
Flash rejects a run at submit when its training context is longer than the base model’s cap. SFT checks train.max_context_tokens; GRPO and OPD check the rollout prompt plus max_completion_tokens. Lower those to at or below the cap - see context window.
A GRPO or OPD run reserves max_completion_tokens out of max_context_tokens, so a context that is not larger than the completion budget leaves nothing for the prompt itself. That is rejected at parse time, before a GPU worker is provisioned:
Raise max_context_tokens above max_completion_tokens, leaving enough room for your longest prompt. Note that thinking = true raises the default completion budget, so enabling it can trip this on a config that previously parsed.
The source adapter is authoritative: omit both train.lora_rank and train.lora_alpha alongside init_from_adapter, and match model_revision exactly. --dry-run resolves and validates the source. See Warm-start safely.
GPU selection is automatic unless you set [gpu] type to an active validated class from flash gpus. Card count is auto-sized only when you omit type and count; [gpu] count and --gpus N pin a ceiling that never escalates, and pinning type alone stays a single-card pin. See Multi-GPU runs.

Run fit and resource use

Expected. GRPO samples multiple completions, scores them, and updates from that group of attempts. For the same model, it usually needs more room and costs more than SFT. To spend less, use a smaller model, reduce max_completion_tokens or max_context_tokens, or start with SFT. Lowering group_size reduces cost too, but it does not make a run that does not fit fit - GRPO’s memory floor is effectively flat in the group. The next entry below covers what to change when a run does not fit.
If you pinned [gpu] count (or --gpus N), that ceiling is the usual cause: the error names the smallest count that would fit, so raise it or drop the pin and let Flash auto-size.When nothing fits even at eight cards, the error names the knobs that actually shrink that algorithm. They are not the same across algorithms:
  • GRPO responds to max_context_tokens, max_completion_tokens, and lora_rank. Required memory is effectively flat in prompts_per_step, so lowering it does not help here.
  • OPD rollout concurrency is prompts_per_step x group_size, so lowering either shrinks the run, as do max_completion_tokens and max_context_tokens. Distillation needs no group variance, so group_size = 1 is fine.
  • SFT responds to max_context_tokens, lora_rank, and batch_size.
A larger base model than the task needs is the other common cause. If you recently enabled thinking = true, reasoning and the final answer share the same token budget.

Training runs

A successful run bills at the quoted Flash cost; a cancelled one is prorated from that quote and capped at it; setup time is not billed. Preview it with flash train config.toml --cost, and see Charges and cancellations.
Expected for GRPO and OPD. Flash sets up your environment and loads the model before the first billed step. flash runs log <run-id> -f prints a warming up (stage=...) line during this phase; it can take several minutes, occasionally 15-20 for a large model, and setup time is not billed. Do not cancel: the run has not stalled. Once training starts, -f streams a per-step heartbeat (reward, grad_norm, kl, entropy, completion length, and more), so a quiet stretch is throttled logging, not a dead run.
Add funds, or lower the pre-flight estimate before submitting again - see Lowering cost.
The task is too hard for the model at its current ability: if no rollout ever scores, there’s nothing for GRPO to reinforce. Try a stronger or larger base model, make the task easier to start, or double-check that your reward returns a positive reward for good answers.
Usually the reward is not discriminative: it scores almost everything the same. Make the reward function separate better answers from worse ones so GRPO has a spread of scores to learn from. See Environments.
When thinking = true, the model emits a reasoning trace before its answer, and that trace counts against the same max_completion_tokens budget as the answer. A max_completion_tokens tuned for a non-thinking run is usually too small once reasoning is added: the reasoning eats the budget and the actual answer is truncated or never emitted. If your reward parses the answer (e.g. extracts a JSON object), it then sees nothing and scores ~0 across the board — even though the model is “working”.Fixes:
  • Raise max_completion_tokens so the reasoning and the answer both fit (e.g. a task that needs ~200 answer tokens may need max_completion_tokens = 2048 with thinking on), and make sure max_context_tokens is large enough to hold the prompt plus that budget.
  • Optionally set thinking_length_penalty_coef to nudge the model toward shorter reasoning so the answer reliably lands inside the budget.
  • Score the answer text by default. In thinking mode response_text remains string-compatible answer text and also exposes response_text.completion, response_text.thinking, and response_text.raw for rewards that intentionally inspect reasoning.
The same trap applies to any reasoning model you call as an LLM judge from a reward: give the judge call enough max_tokens or it returns empty content and the judge silently scores 0.
In Qwen3.5 thinking mode, the chat template treats prior and next assistant turns differently: it strips literal <think> blocks from non-final assistant history, then pre-opens <think>\n in the next generation prompt. A naive multi-turn SFT transcript that puts <think>...</think> in every assistant turn can therefore train on a tag layout that inference will never render. The symptom is doubled, missing, or misplaced thinking tags, or an adapter that behaves differently in training-style evals than it does when served.Fixes:
  • For message-shaped multi-turn SFT targets, keep intermediate assistant turns as the actual code, tool, or action text only.
  • Put <think>...</think> plus the final answer only in the final assistant target.
  • Do not add a second opener for the template’s pre-opened <think>\n. It is already part of the prompt.
OPD refines a model that already has the output format, so warm-start from a finished SFT run and check that the teacher clearly beats the student on held-out examples:
There is no auxiliary EOS loss. For rollouts that reach max_completion_tokens without stopping:
  • Start from an SFT adapter with the desired stopping behavior.
  • Give the teacher a hard answer or reasoning budget in the system prompt.
  • Add stop_sequences when the task has reliable textual terminators.
  • Track truncated_rollouts to diagnose termination.
If the cap is tight enough that most rollouts are cut off, OPD cannot align a teacher signal at all and the run fails naming the cap rather than exiting on an opaque subprocess status:
Raise max_completion_tokens when you see that.OPD supports single- and multi-turn environments but not tool-calling ones, and kl_penalty_coef must be greater than 0.
No. Ctrl-C during flash train detaches you; the run keeps going on Freesolo. Re-follow it any time, and cancel explicitly if you mean to:
Expected. Cancellation waits for the run to stop and release its GPU before confirming, which can take several minutes. The CLI waits that out; the run is marked cancelled when it completes.
Flash retries a limited number of times, resuming from a saved checkpoint where it safely can and failing the run rather than silently restarting from scratch.Watch logs with flash runs log <run-id> -f or poll status with flash runs status <run-id> -f. If the same shape repeatedly fails before useful metrics, see Run fit and resource use for the knobs that shrink your algorithm.

Serving

A run cancelled or preempted before finalizing has no final adapter, so a plain flash models deploy <run-id> cannot serve it. The error lists the run’s saved checkpoint steps and the exact command to use — deploy one of them instead:
Expected: every deploy runs a bounded smoke before the alias routes to the new revision, and a large model takes a few minutes to warm and verify. A failed smoke leaves the alias unchanged. See Deploy & chat.
Serving is billed per token for requests. Prompt, completion, and cached prompt token rates are listed in Supported models. flash models undeploy <run-id> disables the alias and immutable revisions.
Use openai_base_url from flash models deployments --json, set model to the run alias or a full immutable revision, and pass your Freesolo API key.
  • 401: missing or invalid key.
  • 402: insufficient org balance.
  • 403: the key’s org does not own the adapter.
  • 503: temporarily unavailable; retry with backoff.
See Use it from your own code.

Getting help

Still stuck? Reach out through freesolo.co/contact with the run id (flash runs), the failing command, and its --debug traceback.