# The qsolve command line

> Reference for every qsolve command, argument and option: init, validate, lint, run, estimate and test, with defaults, exit codes, output formats, environment variables, error messages and examples.

Source: https://qsolve.omlabs.co/docs/build/cli
Updated: 2026-11-13

`qsolve` is installed with `qsolve-sdk` (`pip install "qsolve-sdk[ibm]"`). It is a [Typer](https://typer.tiangolo.com) application with six commands, all working on a workflow directory on your machine. The platform runs the same SDK code, so these are the checks it applies. The Python behind each command is documented in the [SDK reference](/docs/build/sdk-reference).

| Command | Purpose |
|---|---|
| [`init`](#qsolve-init) | Scaffold a working Qiskit workflow (a QUBO solved with QAOA) to start from. |
| [`validate`](#qsolve-validate) | Check the manifest, schemas and example inputs. |
| [`lint`](#qsolve-lint) | Static checks the platform applies before running anything. No code is executed. |
| [`run`](#qsolve-run) | Run a workflow locally on one input. |
| [`estimate`](#qsolve-estimate) | Size a run (qubits, circuits, shots) without executing it. |
| [`test`](#qsolve-test) | Run the validation gates and benchmark cases, the same ones the platform runs. |

## Global options

```text
qsolve [OPTIONS] COMMAND [ARGS]...
```

| Option | Meaning |
|---|---|
| `--version` | Print the SDK version (for example `1.0.0`) and exit 0. |
| `--install-completion` | Install shell completion for the current shell. |
| `--show-completion` | Print the completion script, to copy or customise. |
| `--help` | Show help and exit 0. Also available on every command: `qsolve run --help`. |

Running `qsolve` with no command prints the help and exits 2 (the usage-error code of the Click library under Typer).

## Targets

The `--target` option of `run`, `estimate` and `test` takes one of:

| Value | Runs on |
|---|---|
| `sim-cpu` | Qiskit Aer on the CPU. The default. Noise-free. |
| `sim-gpu` | Qiskit Aer on a GPU; needs `qiskit-aer-gpu`. |
| `fake:<device>[@scale]` | Aer with an IBM device's published noise model (`fake:fez`), optionally with every error rate multiplied by `scale`, from 1 to 10 (`fake:fez@1.6`). Free. Needs the `ibm` extra. |
| `ibm:<device>` | IBM Quantum hardware, with your own token (`ibm:ibm_fez`). Needs the `ibm` extra. Spends your IBM quota. |

See [Backends and targets](/docs/build/backends-and-targets). `estimate` does not run anything, so its `--target` is only passed to your workflow's `estimate()`; it is not checked.

## Environment variables

| Variable | Used by | Purpose |
|---|---|---|
| `QSOLVE_IBM_TOKEN` | `--target ibm:<device>` | Your IBM Quantum token. Required for that target. Never put it in workflow code or the repository. |
| `QSOLVE_IBM_INSTANCE` | `--target ibm:<device>` | Optional IBM instance. |
| `QSOLVE_SIM_THREADS` | simulators | Threads a simulation may use. A positive integer; otherwise every core available to the process. |
| `QSOLVE_EXECUTOR_ADDR`, `QSOLVE_EXECUTOR_TOKEN` | the platform's sandbox | Set by the platform, not by you. When `QSOLVE_EXECUTOR_ADDR` is set, every run uses the platform's executor whatever `--target` says. Leave it unset locally. |

## Exit codes

| Code | Meaning |
|---|---|
| `0` | The command succeeded. |
| `1` | The command ran and found a problem (see each command below), or an unexpected exception: Python prints a traceback and exits 1. A missing or invalid `qsolve.yaml`, an unreadable input file and a bad target are never tracebacks: they print `Error:` or `Invalid workflow:` and exit 1. |
| `2` | A usage error caught by the command-line parser: a missing required option or argument, an unknown option, or a malformed `--param`. |

| Command | 0 | 1 |
|---|---|---|
| `init` | the workflow was created | invalid name, or the destination exists |
| `validate` | manifest, schemas and examples are valid | any of them is invalid |
| `lint` | no errors; warnings allowed | at least one error |
| `run` | the run completed | a `WorkflowError` such as an invalid input, parameter or target; an unreadable input file; a missing or invalid `qsolve.yaml` |
| `estimate` | the estimate was printed | the same causes as `run` |
| `test` | every gate passed | a gate failed, or the workflow is invalid |

## qsolve init

```text
qsolve init NAME [--summary TEXT] [--directory PATH]
```

Creates a complete workflow in `<directory>/<NAME>` that solves a small QUBO with QAOA and passes every gate.

| Argument or option | Default | Meaning |
|---|---|---|
| `NAME` | required | 2 to 63 lowercase letters, digits and dashes, starting with a letter or digit (regular expression `[a-z0-9][a-z0-9-]{1,62}`). It becomes the workflow's name in `qsolve.yaml`. |
| `--summary` | `My Qsolve workflow` | One-line description, written into `qsolve.yaml` (`summary:`) and the README. Whitespace runs, line breaks included, become single spaces. The value is written as a quoted YAML string, so colons, `#`, quotes and a leading dash are safe. |
| `--directory` | the current directory | The parent directory. Created if missing. |

Files created:

```text
<NAME>/
  qsolve.yaml               the manifest, with name and summary filled in
  README.md                 a short readme with the develop commands
  benchmarks/cases.yaml     three cases: two noise-free, one on fake:fez@1.6
  examples/qubo_5.json      a 5-variable example input
  schemas/input.json
  schemas/output.json
  src/__init__.py
  src/workflow.py           the Qiskit workflow (class Qubo)
  tests/test_workflow.py    a pytest file that runs the example through run_workflow
```

It creates no `LICENSE` and no `requirements.lock`, so `qsolve lint` warns `W051` until you add a license file. File meanings: [Project layout](/docs/build/project-layout). Walk-through: [Your first workflow](/docs/build/your-first-workflow).

On success it prints:

```text
Created <path>
Next: cd <NAME> && qsolve test .   (edit src/workflow.py: it is plain Qiskit)
```

| Message (printed in red) | Exit | Cause |
|---|---|---|
| `Name must be lowercase letters, digits and dashes (2-63 chars).` | 1 | `NAME` does not match the pattern (uppercase, underscore, one character, 64 or more). |
| `<path> already exists.` | 1 | The destination exists, as a file or a directory. Nothing is overwritten. |

The summary and the name are written into `qsolve.yaml` as quoted YAML strings, so `--summary "Pick: the depots # fast"` is stored exactly as typed.

## qsolve validate

```text
qsolve validate [WORKFLOW]
```

| Argument | Default | Meaning |
|---|---|---|
| `WORKFLOW` | `.` | The workflow directory. |

Loads `qsolve.yaml`, reads both schema files and checks every example input listed under `inputs.examples` against the input schema. Prints, in green:

```text
OK depot-picker v0.1.0
```

| Message (red) | Exit | Cause |
|---|---|---|
| `Invalid workflow: <reason>` | 1 | No `qsolve.yaml` (`No qsolve.yaml in <dir>`), invalid YAML, a manifest that breaks a rule (the reason is the pydantic error text), a schema file missing or not a JSON object, or a schema path that leaves the directory. |
| `Example <path>: <reason>` | 1 | An example file is unreadable (`cannot read <path>: ...`), is not a JSON object, or does not match the input schema (`Invalid input at <field>: <message>`). It stops at the first bad example. |

`validate` does not check the schemas against Qsolve's restrictions and does not inspect your code; `lint` does that. It does not require that at least one example exists; `lint` and `test` do.

## qsolve lint

```text
qsolve lint [WORKFLOW]
```

| Argument | Default | Meaning |
|---|---|---|
| `WORKFLOW` | `.` | The workflow directory. |

Runs the static checks the platform applies before it executes anything. None of your code runs. Each finding is one line: the severity (upper case, padded to 7 characters), the code, the message and, when there is one, the path (relative to the workflow, shown dim):

```text
WARNING W051 no LICENSE file found
0 error(s), 1 warning(s)
```

With no findings it prints `No problems found`. Otherwise the last line is `<n> error(s), <m> warning(s)`. Errors print in red, warnings in yellow. It exits 1 when there is at least one error; warnings alone exit 0. A missing or invalid manifest is reported as a finding (`E001`, `E002`), not as a crash. Every code is explained in [Lint codes](/docs/build/lint-codes).

## qsolve run

```text
qsolve run WORKFLOW --input FILE [--target T] [--seed N] [--param key=value]... [--out FILE]
```

Runs the workflow once on one input, with every declared method.

| Argument or option | Short | Default | Meaning |
|---|---|---|---|
| `WORKFLOW` | | required | The workflow directory. |
| `--input` | `-i` | required | Path to the input JSON file. It must contain a JSON object. |
| `--target` | | `sim-cpu` | Where to run; see [Targets](#targets). |
| `--seed` | | `0` | The run's seed (an integer). The same input, parameters and seed give the same result. |
| `--param` | `-p` | none | A parameter override, `key=value`. Repeatable. The value is parsed as JSON where possible, so `500` is a number, `0.5` a float, `true` a boolean and `[1,2]` a list, while `fast` stays text. The key must be a parameter declared in the manifest. |
| `--out` | | none | Write the full JSON output, the result and its provenance, to this file (UTF-8, indented). |

```bash
qsolve run . -i examples/qubo_5.json --seed 1 -p layers=1
```

```text
            depot-picker v0.1.0 on aer-cpu
┌───────────────────────────────┬─────────────────────┐
│ Metric                        │ Value               │
├───────────────────────────────┼─────────────────────┤
│ Objective (quantum)           │ -10                 │
│ Objective (Exhaustive search) │ -10                 │
│ Gap vs baseline               │ 0.000%              │
│ Uncertainty                   │ exact (stderr None) │
│ Shots                         │ 4000                │
│ Run time (s)                  │ 2.22                │
└───────────────────────────────┴─────────────────────┘
QAOA (quantum approximate optimisation): -10; Exhaustive search: -10 (accuracy 100.00%).
```

The table title is `<workflow> v<version> on <device>`. The row labelled with the reference is named after the reference method's manifest title. After the table it prints the result's summary sentence, and `Wrote <path>` when `--out` was given.

The `--out` file is the `RunOutput` as JSON: a `result` object (answer, objectives, gap, uncertainty, per-method results, metrics, quality measurements) and a `provenance` object (input hash, parameters, seed, target, device, shots, timings, result hash). Every field is described in [Results and events](/docs/build/sdk-results-and-events).

| Message | Exit | Cause |
|---|---|---|
| `Error: <message>` (red) | 1 | Any `WorkflowError`. Common ones below. |
| `Error: cannot read <file>: <reason>` | 1 | The input file is missing or not valid JSON. |
| `Error: <file> must contain a JSON object` | 1 | The input JSON is not an object. |
| `Error: unknown parameters: ['x']` | 1 | `--param` names a parameter the manifest does not declare. |
| `Error: parameter 'layers' must be <= 6.0` (or `>= ...`, `must be an int`, `must be a number`, `must be one of [...]`) | 1 | A parameter value is out of range or the wrong type. |
| `Error: Invalid input at Q: [[1]] is too short` | 1 | The input does not match the input schema; the path names the field. |
| `Error: Unknown target 'foo'. Use sim-cpu, sim-gpu, ibm:<device> or fake:<device>[@scale].` | 1 | A bad target. |
| `Error: No qsolve.yaml in <dir>` (or the manifest's validation message) | 1 | `run` and `estimate` on a directory with a missing or invalid `qsolve.yaml`: the same causes as `validate`'s `Invalid workflow:`. |
| `Error: QSOLVE_IBM_TOKEN is not set` | 1 | `ibm:` target without a token. |
| `Error: Install the ibm extra: pip install 'qsolve-sdk[ibm]'` | 1 | `ibm:` or `fake:` target without the extra. |
| `Error: no fake device 'x' (e.g. fez, marrakesh, torino)` | 1 | Unknown fake device. |
| `Error: noise scale must be between 1 and 10` | 1 | `fake:<device>@<scale>` out of range. |
| `Error: Problem has N variables; this workflow supports at most M (<notes>)` | 1 | The input is bigger than the manifest's envelope. |
| `Error: the quantum method or the reference produced no result: <why>` | 1 | A method was unavailable or raised `MethodUnavailable`. |
| `Error: Workflow produced an invalid answer: <message>` | 1 | `answer()` returned something the output schema rejects. |
| `Error: --param expects key=value, got 'foo'` | 2 | A malformed `--param`; printed by the usage-error formatter. |

An unexpected exception in your own code, or a workflow directory with no valid `qsolve.yaml` (use `validate` first), is not caught by `run` and prints a Python traceback, exit code 1.

## qsolve estimate

```text
qsolve estimate WORKFLOW --input FILE [--target T]
```

Validates the input and sizes the run without executing any method (no circuit runs), printing the estimate as JSON.

| Argument or option | Short | Default | Meaning |
|---|---|---|---|
| `WORKFLOW` | | required | The workflow directory. |
| `--input` | `-i` | required | Path to the input JSON file (a JSON object). |
| `--target` | | `sim-cpu` | Passed to your workflow's `estimate(target)`; not checked. |

```json
{
  "qubits": 5,
  "circuits_on_target": 1,
  "shots_on_target": 4000,
  "classical_cost_class": "low",
  "runtime_s": null,
  "notes": [],
  "by_default": false
}
```

`by_default` is `true` when the workflow gives no `estimate()` and Qsolve fell back to one circuit and `params["shots"]` shots. `runtime_s`, when the workflow predicts it, includes the fixed 20 s run overhead. **There is no `--param` option, so the estimate uses the manifest defaults.** Errors are the same `Error: <message>` lines as for `run` (invalid input, envelope, and so on), exit 1. The fields are explained in [Metering and cost](/docs/build/sdk-metering-and-cost#estimate).

## qsolve test

```text
qsolve test [WORKFLOW] [--target T] [--out FILE]
```

Runs the validation gates and every benchmark case, as the platform does on submission.

| Argument or option | Default | Meaning |
|---|---|---|
| `WORKFLOW` | `.` | The workflow directory. |
| `--target` | `sim-cpu` | The target for cases that do not set their own `target`. |
| `--out` | none | Write the full JSON report here (UTF-8, indented). |

It prints a table of gates (name, `pass` or `FAIL`, detail), a table of cases titled `Cases on <target>` (name, result, relative gap, mitigated fidelity, seconds; `-` for a missing value; a failed case shows its reason after `FAIL`), then `ALL GATES PASSED` in green or `FAILED` in red. See [Your first workflow](/docs/build/your-first-workflow) for a real output and [Benchmarks](/docs/build/benchmarks) for each gate and `expect` key. The JSON report holds `workflow`, `version`, `target`, `passed`, `gates`, `cases` and `total_time_s` ([`BenchmarkReport`](/docs/build/sdk-testing-and-benchmarks#benchmarkreport)).

| Message | Exit | Cause |
|---|---|---|
| `ALL GATES PASSED` | 0 | Every gate passed. |
| `FAILED` | 1 | At least one gate failed. |
| `Invalid workflow: <reason>` (red) | 1 | No `qsolve.yaml`, an invalid manifest, a missing schema, or a malformed `cases.yaml`. No tables are printed. |

A target that cannot be used (for example `ibm:` without a token) does not stop the command; each affected case fails with the reason in its row. `qsolve test` runs every case once, then the first case again and an estimate, so with `--target ibm:<device>` it spends your own IBM quota. `qsolve test` does not run lint; run `qsolve lint .` too, because the platform runs both. Cases listed under `hardware_cases:` are never run by `qsolve test`.

## A typical loop

```bash
qsolve validate . && qsolve lint . && qsolve test .
```

Because every command exits non-zero on failure, this stops at the first problem. Run it before every submission; see [Validation and publishing](/docs/build/validation-and-publishing).

## The same checks from Python

| Command | Python |
|---|---|
| `qsolve run` | `qsolve.run_workflow(dir, input, target=, params=, seed=)` |
| `qsolve estimate` | `qsolve.estimate_run(dir, input, target=)` |
| `qsolve validate` | `qsolve.load_workflow(dir)` and `qsolve.runner.validate_input` |
| `qsolve lint` | `qsolve.lint.lint_workflow(dir)` |
| `qsolve test` | `qsolve.benchmark.run_benchmarks(dir, target)` |
