# Lint codes

> Every error and warning code that qsolve lint and the submission's static checks can report, what triggers it, the message and how to fix it.

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

`qsolve lint .` runs static checks on a workflow repository. No code of yours is executed. The platform runs the same checks in the `lint` stage of a submission, before anything is built or run. Lint is defence in depth: the sandbox, not the linter, is the security boundary.

- Errors (`E`) fail lint and reject a submission.
- Warnings (`W`) are reported and never reject a submission.

Each finding has a severity, a code, a message and a path. If the manifest cannot be loaded, lint stops after reporting `E001` or `E002` (plus any `E010` and `E011` found while listing files), because the later checks need the manifest. Fix those first and run lint again. For how lint fits in the pipeline, see [Validation and publishing](/docs/build/validation-and-publishing). For gate and run messages, see [Troubleshooting](/docs/build/troubleshooting).

## Files scanned

Lint walks every file under the workflow folder, except any path that has one of these folders in it: `__pycache__`, `.git`, `.venv`, `.pytest_cache`, `.ruff_cache`, `.mypy_cache`, `node_modules`. Python files under a folder named `tests` are not checked for imports, because tests are never shipped or run. The secret scan (`E012`, `E013`) covers every file, including tests.

## Manifest and structure

| Code | Severity | Triggers when | Message | Fix |
|---|---|---|---|---|
| `E001` | Error | There is no `qsolve.yaml` file at the workflow root. | `qsolve.yaml not found at the repository root` | Put `qsolve.yaml` at the repository root, or enter its folder in the **Folder** field when you submit. |
| `E002` | Error | Loading the workflow raises a validation error, a value error or a JSON error: `qsolve.yaml` is not valid YAML, a field is missing, unknown or wrong, a schema path escapes the folder, a schema file is missing, a schema file is not a JSON object or not valid JSON. | `invalid workflow definition: <reason>` | Run `qsolve validate .` for the field and reason. See [Manifest](/docs/build/manifest). |
| `E030` | Error | The module in `entrypoint` (`package.module:Class`) is not found at `<module>.py` or `<module>/__init__.py` under the root or under `src/`; or the module has no top-level class, function or assignment named `Class`. An `entrypoint` module that is `qsolve` or starts with `qsolve.` is skipped. | `entrypoint: module 'x' not found in the repository` or `entrypoint: 'X' is not defined in x.py` | Match `entrypoint` to a file and a top-level class, for example `src.workflow:MaxCut` needs `src/workflow.py` defining `class MaxCut`. |
| `E031` | Error | The entrypoint module has a syntax error, is not valid UTF-8 or cannot be read, or any `.py` file outside `tests` cannot be parsed (syntax error, not UTF-8, null bytes). | `x.py: syntax error line N: <reason>`, `x.py: cannot parse Python: <reason>` or `cannot parse Python: <reason>` | Fix the syntax at the line shown. |

## Schemas and example inputs

| Code | Severity | Triggers when | Message | Fix |
|---|---|---|---|---|
| `E020` | Error | The input or output schema is not a valid JSON Schema draft-07. | `invalid JSON Schema: <reason>` | Fix the schema. See [Input schema reference](/docs/build/input-schema-reference) and [Output schema reference](/docs/build/output-schema-reference). |
| `E021` | Error | Anywhere in a schema, at any depth, a keyword is `pattern` or `patternProperties`; or a `$ref` string does not start with `#`; or `format` is `regex`. One finding per occurrence, with its location in the path. | `'pattern' is not allowed in input/output schemas (regular expressions can be abused to hang validation); use enum, minLength or maxLength instead`, `only local '#/...' $ref values are allowed`, or `format 'regex' is not allowed` | Use `enum`, `minLength` or `maxLength`; point `$ref` at `#/definitions/...`. Only schema keywords are checked: a property that is merely *named* `pattern` (a key under `properties`, `definitions` or `$defs`), and the data in `required`, `enum`, `const`, `default` and `examples`, are not flagged. |
| `E022` | Error | The schema, serialised to JSON, is longer than 262,144 characters (256 KiB). | `schema is too large` | Shrink the schema. |
| `E023` | Error | An example listed in `inputs.examples` cannot be read or is not JSON, or does not validate against the input schema. If the schema itself is invalid, this is skipped and `E020` reports. | `example input unreadable: <reason>` or `example input does not match the input schema: <reason>` | Fix the file path, the example, or the schema. `qsolve validate .` shows the failing path. |
| `E024` | Error | `inputs.examples` is empty. | `at least one example input is required` | List at least one file under `inputs.examples`. |
| `E025` | Error | A parameter named `shots` of type `int` has no `max`, a `max` above 100,000 or a `default` above 100,000. The platform runs at most 100,000 shots per circuit (`qsolve.MAX_SHOTS_PER_CIRCUIT`) and refuses a larger request, so the workflow would offer values that always fail. | `parameter 'shots' allows up to N (default M) but the platform runs at most 100,000 shots per circuit: set max (and default) to 100000 or less` | Set `max` (and `default`) to 100000 or less. |
| `W052` | Warning | `targets.min_qubits_qpu` is set. The manifest accepts it, but the platform currently ignores it. | `targets.min_qubits_qpu is currently ignored by the platform` | Remove it, or keep it for when it is enforced. It does not affect any run today. |

## Repository files

| Code | Severity | Triggers when | Message | Fix |
|---|---|---|---|---|
| `E010` | Error | A path in the repository is a symbolic link. One finding per link. | `symbolic links are not allowed` | Replace the link with a real file. |
| `E011` | Error | The repository has more than 500 files, or the files add up to more than 20 MiB (20,971,520 bytes). Either condition gives its own finding. | `too many files (N > 500)` or `repository too large (N > 20971520 bytes)` | Remove files and data you do not need. |
| `E012` | Error | A file path matches a credential pattern: `.env` or `.env.*` (`.env.example`, `.env.sample` and `.env.template` are allowed, and still get the secret scan `E013`), `id_rsa*`, `id_ed25519*`, `*.pem`, `*.p12`, `*.pfx`, `*.key`, `credentials*.json`, `.netrc`. | `file looks like a credential; remove it and rotate the secret` | Delete the file, and revoke the credential if it was real. A template named `.env.example`, `.env.sample` or `.env.template` is fine. |
| `E013` | Error | The text of a file contains one of: a private key header (`-----BEGIN [RSA \|EC \|OPENSSH \|DSA ]PRIVATE KEY-----`); an AWS access key (`AKIA` plus 16 capitals or digits); a GitHub token (`ghp_`, `gho_`, `ghu_`, `ghs_` or `ghr_` plus 30 or more characters); a Supabase or Stripe style secret (`sb_secret_`, `sk_live_` or `sk_test_` plus 16 or more characters); an IBM Quantum token assignment (a name starting `ibm` or `qiskit` and containing `token`, set to a quoted string of 30 or more characters). Files over 1 MiB, files ending in `.png .jpg .gif .pdf .zip .npy .npz`, and files that are not UTF-8 are not scanned. | `possible <kind> committed; remove it and rotate the secret` | Remove it and rotate the secret. A workflow never needs credentials. |
| `W051` | Warning | None of `LICENSE`, `LICENSE.md`, `LICENSE.txt`, `COPYING` exists at the root. | `no LICENSE file found` | Add a `LICENSE` file. |
| `W060` | Warning | There is no `README.md` at the root. | `no README.md; describe what the workflow solves and its limits` | Add a README. |

## Code and imports

Checked on every `.py` file outside a `tests` folder.

| Code | Severity | Triggers when | Message | Fix |
|---|---|---|---|---|
| `E040` | Error | An absolute import (`import x` or `from x import y`) names a top-level module that is not the standard library, not a module or package of your own repository (at the root or under `src/`, or `src`), not `__future__`, not on the allowed list, and not pinned in `requirements.lock`. Relative imports are not checked. | `imports 'x', which is not available in the runtime. Allowed: the Python standard library and <list of the allowed modules>` | Import only the standard library and `qsolve`, `numpy`, `scipy`, `sklearn`, `qiskit`, `qiskit_aer`, `pyscf`, `pydantic`, `jsonschema`, `yaml`, `typer`, `rich`. There is no network in the sandbox, so nothing else can be installed. |
| `W041` | Warning | An import of `subprocess`, `socket`, `ctypes`, `pickle`, `marshal`, `shelve` or `multiprocessing`; or a direct call to `eval()`, `exec()`, `compile()` or `__import__()`. Each import and each call gives a finding. | `imports 'x' (blocked or useless in the sandbox; flagged for review)` or `calls eval() (flagged for review)` | Remove them. They are blocked or useless in the sandbox and flagged for review. |
| `W042` | Warning | An import that would be `E040` is pinned in your `requirements.lock` instead. It is reported as this warning and not as `E040`. | `imports 'x', pinned in requirements.lock but not in the runtime image, so the sandbox will reject it` | The sandbox rejects the import at run time. Remove the dependency or replace it. See [Project layout](/docs/build/project-layout). |
| `W043` | Warning | A call to something named `import_module` or `__import__` as an attribute, such as `importlib.import_module(...)`. | `imports a module by name at run time (not checked: the runtime image decides what is importable)` | Prefer ordinary imports so lint can check them. |

Names in `requirements.lock` are read as the text before `==`, `>=`, `~=`, `<=`, an extras bracket or the end of the line, with `-` and `.` turned into `_`. Four distribution names are mapped to their import names: `scikit_learn` to `sklearn`, `pyyaml` to `yaml`, `pillow` to `PIL`, `opencv_python` to `cv2`. Any other package whose import name differs from its distribution name would be seen under the distribution name.

## Benchmarks and hardware cases

| Code | Severity | Triggers when | Message | Fix |
|---|---|---|---|---|
| `E050` | Error | `benchmarks/cases.yaml` does not exist. | `benchmarks/cases.yaml is required: the platform benchmarks every workflow` | Add the file. See [Benchmarks](/docs/build/benchmarks). |
| `E052` | Error | The `hardware_cases` list cannot be loaded (invalid YAML, an unknown key, a wrong type, a missing `name`, a case that does not match the case format); or a hardware case has no `target` or one that does not match `ibm:<device>` (letters, digits and `_ . -`, 1 to 60 characters). One finding for the whole list when it cannot be loaded, one per bad case otherwise. | `hardware_cases are invalid: <reason>` (cut at 300 characters) or `hardware case 'x' needs target: ibm:<device>, for example ibm:ibm_fez` | Fix the entry. |
| `E053` | Error | There are more than 3 `hardware_cases`. | `at most 3 hardware_cases are allowed` | Keep at most three. |
| `E054` | Error | There is at least one hardware case and `targets.qpu` in the manifest does not contain `ibm`. | `hardware_cases need `qpu: [ibm]` under targets` | Add `ibm` to `targets.qpu`. See [Backends and targets](/docs/build/backends-and-targets). |
| `E055` | Error | A case under `cases:` is malformed (an unknown key, a wrong type, no `name`), `cases.yaml` is not a mapping, or a case has neither `input` nor `input_file`. Also applies to `hardware_cases`: a hardware case with no input. (A file that is not valid YAML is `E052`.) | `cases are invalid: <reason>` or `case 'x' needs `input` or `input_file`` | Give every case an `input` or an `input_file`. |
| `E056` | Error | A case's `input_file` is an absolute path, contains `..`, resolves outside the repository (including through a symbolic link), does not exist, is not valid JSON, or is not a JSON object. Checked for `cases` and `hardware_cases`. | `case 'x': input_file 'p' must be a relative path inside the repository (no absolute path, no '..')`, `... not found` or `... unreadable: <reason>` | Put the input file inside the repository and use a relative path. |
| `E057` | Error | Two cases share a name, counting `cases` and `hardware_cases` together. | `duplicate case name 'x'` | Give every case a unique name. |
| `E058` | Error | A case under `cases:` has a `target` starting `ibm:`. Cases run on every validation; a real device belongs under `hardware_cases`. | `case 'x' targets ibm:...: a real device belongs under `hardware_cases`, not `cases` (which run on every validation)` | Move the case to `hardware_cases`, or use `fake:<device>` to rehearse. |
| `E059` | Error | A case's input (inline or from its file) does not match the input schema. Skipped when the schema itself is invalid (`E020`). | `case 'x': input does not match the input schema: <reason>` | Fix the case input or the schema. |

## License

| Code | Severity | Triggers when | Message | Fix |
|---|---|---|---|---|
| `E051` | Error | The manifest's `license` is empty or only spaces. | `a license identifier is required` | Set `license` in `qsolve.yaml`, for example `Apache-2.0`. |

## Codes that do not exist

This page lists every code the SDK's lint emits: 25 errors (`E001`, `E002`, `E010` to `E013`, `E020` to `E025`, `E030`, `E031`, `E040`, `E050` to `E059`) and 6 warnings (`W041`, `W042`, `W043`, `W051`, `W052`, `W060`). A code that is not here comes from a newer SDK or another tool: update with `pip install -U qsolve-sdk`.
