Skip to content

Analyzing Code

robotcode analyze code performs static analysis on a Robot Framework project: it parses the .robot and .resource files, resolves their imports, libraries, and variables, and reports diagnostics for problems such as unknown keywords, unresolved variables, wrong argument counts, duplicate or failing imports, and deprecated syntax. No tests are executed.

The analysis is performed by the same code that powers the RobotCode language server, so the diagnostics are identical to the ones shown in the editor (the VS Code extension, the IntelliJ plugin, or any other LSP client); analyze code applies that analysis to the whole project from the command line. Configuration and profile resolution work the same as for robotcode robot, so imports and variables resolve as they would at run time. The result is printed as a human-readable list or, for CI use, as a structured report (JSON, SARIF, GitHub annotations, or GitLab Code Quality).

Which diagnostics are reported, and at what severity, can be configured per project or inline in the source; this is shared with the language server and documented separately in Controlling Diagnostics with Modifiers.

Who this is for:

  • Developers who want a fast "is my project sound?" check before committing or running anything.
  • CI/CD pipelines that want to fail a build on broken keyword references or upload findings to a code-scanning dashboard.
  • Code review / security tooling — the SARIF output plugs straight into GitHub code scanning; the GitLab format into Merge Request Code Quality widgets.
  • AI-driven workflows — a coding agent gets a precise, machine-readable list of what's wrong instead of having to run the suite and parse a traceback.

This guide covers analyze code. The analyze group has one other subcommand, cache, for inspecting and clearing the on-disk analysis cache — covered briefly at the end.

For the exhaustive, auto-generated option list see the CLI reference.

Terminal window
# Analyze the whole project
robotcode analyze code
# Analyze a specific folder or file
robotcode analyze code tests/acceptance
robotcode analyze code tests/acceptance/login.robot
# Only look at certain files
robotcode analyze code --filter "**/*.resource"
# Also report unused keywords and variables
robotcode analyze code --collect-unused
# Treat a specific diagnostic as ignored / as an error
robotcode analyze code -mi MultipleKeywords
robotcode analyze code -me VariableNotFound
# Machine-readable output
robotcode analyze code --output-format json
robotcode analyze code --output-format sarif --output-file robotcode.sarif

If you don't pass any paths, analyze code analyzes the whole project — every .robot / .resource file under the project root, minus the configured exclude patterns, the paths excluded by .robotignore or .gitignore, and hidden files and folders. (This is unlike robotcode robot, which would run only the default paths/suites from your profile.)

analyze code reuses the full RobotCode configuration pipeline:

  1. It locates the project root and the robot.toml / pyproject.toml files the same way every other robotcode command does.
  2. It applies the active profile(s) (--profile) and the [tool.robotcode-analyze] settings.
  3. It honours --variable, --variablefile and --pythonpath (mirroring the corresponding robot options) so that imports and variable resolution behave exactly as they would at run time.

This matters: a keyword that only resolves because of a --pythonpath entry or a variable file resolves during analysis too, instead of being wrongly reported as missing.

Imports and libraries are resolved only for the files selected by paths / --filter, and the per-file diagnostics are computed only for those files, so a narrower selection makes a run faster.

The diagnostics are exactly those the language server produces. Among the most common:

  • KeywordNotFound — a keyword call that resolves to nothing.
  • MultipleKeywords — an ambiguous keyword name matching several definitions.
  • VariableNotFound / VariableNotReplaced — references to variables that don't exist or can't be resolved statically.
  • import diagnostics — LibraryAlreadyImported, ResourceAlreadyImported, ImportContainsErrors, circular imports, LibraryLoadedWithoutArguments (information: the library could not be initialized with the import's arguments, so its keywords come from loading it without arguments), LibraryImportIgnored (warning, Robot Framework 7.4+: Robot Framework ignores the library import, because an earlier import already uses its name for another library or with other arguments), …
  • deprecation warnings — DeprecatedReturnSetting, DeprecatedHeader, DeprecatedHyphenTag, TagsWithoutEmptyRow (a Tags: section in a keyword's documentation without an empty row before it, Robot Framework 7.5+), …
  • model errors — empty tests/keywords, invalid headers, and other structural problems.

This is static analysis, so it isn't the last word: actually running the suite — or even discovering it with robotcode discover — can surface errors that analyze code doesn't. Building the real test suite runs additional, suite-wide checks (for example duplicate test case names), and a PreRunModifier may have changed the suite before it's executed — neither of which the per-file static analysis sees. A clean analyze code result is a strong signal, not a guarantee that a run will start without errors.

By default analyze code does not flag unused definitions. Unlike the per-file diagnostics, deciding whether a keyword or variable is unused needs the reference data of the whole project: with paths / --filter, only references inside the selected files count, and anything used only outside them is reported as unused, so run it on the whole project. It also adds an extra pass that is noticeably slower on projects with many keywords and variables. Enable it explicitly:

Terminal window
robotcode analyze code --collect-unused

This adds two diagnostics:

  • KeywordNotUsed — a keyword defined in the project that nothing references.
  • VariableNotUsed — a variable/argument that's never read. Names that are intentionally unused (${_}, ${_ignored}) are skipped.

They behave like any other diagnostic — you can remap or ignore them with the modifiers (e.g. -mh KeywordNotUsed) or # robotcode: comments.

You can also turn this on permanently in robot.toml:

[tool.robotcode-analyze.code]
collect-unused = true

Every diagnostic has one of four severities — Error, Warning, Information, Hint — and you can remap any diagnostic code to a different severity (or silence it entirely). On the command line:

Flag Effect
-mi, --modifiers-ignore CODE Ignore this diagnostic code entirely
-me, --modifiers-error CODE Treat as Error
-mw, --modifiers-warning CODE Treat as Warning
-mI, --modifiers-information CODE Treat as Information
-mh, --modifiers-hint CODE Treat as Hint

Each flag can be given multiple times. The same mapping is available — and usually better kept — in robot.toml:

[tool.robotcode-analyze.modifiers]
ignore = ["VariableNotFound"]
error = ["MultipleKeywords"]
hint = ["KeywordNotUsed"]

* is a wildcard that matches every code, so -mi "*" silences all diagnostics. Re-introducing specific codes at a chosen severity then gives you an allow-list:

Terminal window
# show only KeywordNotFound — reported as a warning
robotcode analyze code -mi "*" -mw KeywordNotFound

This is a remap (the kept codes take the severity you assign). If you instead want to keep a code at its original severity, use the --code filter.

Because the severity is configurable, the text and SARIF/GitHub/GitLab output always carries the effective severity, not a hard-coded one. For the full modifier system — including inline # robotcode: comments — see Controlling Diagnostics with Modifiers.

Two flags narrow the result to a subset. Unlike the modifiers above (which remap a code's severity), these filter: whatever you don't list is dropped from the output, the summary and the exit code alike — the severity of what remains is unchanged.

  • --severity — keep only the given severities. Values: error, warn/warning, info/information, hint.
  • --code — keep only the given diagnostic codes (e.g. KeywordNotFound); matching is case-insensitive.

Both are repeatable and comma-separated. When both are given they combine with AND — a diagnostic must match both.

Terminal window
robotcode analyze code --severity warning,hint # only warnings and hints
robotcode analyze code --code KeywordNotFound # only KeywordNotFound, with its real severity
robotcode analyze code --code KeywordNotUsed --severity hint # only KeywordNotUsed that are hints

When neither is given, everything is reported.

analyze code exits with a bitwise combination of these values, so a single status tells you which severities were present:

Value Name Meaning
0 SUCCESS nothing reported
1 ERRORS at least one error
2 WARNINGS at least one warning
4 INFOS at least one information
8 HINTS at least one hint

For example, errors and warnings give 1 | 2 = 3.

To stop certain severities from affecting the exit code — e.g. so warnings don't fail the build — use an exit-code mask:

Terminal window
# Warnings/infos/hints no longer change the exit code; only errors do
robotcode analyze code --exit-code-mask warn,info,hint
# Same, but additive to the mask configured in robot.toml
robotcode analyze code --extend-exit-code-mask hint

Valid mask values: error, warn (alias warning), info (alias information), hint, all. In robot.toml:

[tool.robotcode-analyze.code]
exit-code-mask = ["warn", "info", "hint"] # CI fails only on errors

A non-zero exit means that something was reported, which is what makes a CI step fail by default, or that the command failed: a usage error, such as an invalid option value or a missing --output-file directory, exits with 2, and other failures, such as an invalid robot.toml, exit with 1. To branch on a specific severity, make sure a report was written, then test the corresponding bit — in bash:

Terminal window
robotcode analyze code | tee analyze.txt
code=${PIPESTATUS[0]}
[ -s analyze.txt ] || exit "$code" # nothing written: the command itself failed
(( code & 1 )) && echo "errors present"
(( code & 2 )) && echo "warnings present"

The local --output-format flag selects how results are rendered. It overrides the global --format for this command:

Value Output
concise (default) Human-readable list, one finding per line, sorted by (file, line, column)
json Compact structured result (diagnostics + summary)
json-indent Same, pretty-printed
sarif SARIF 2.1.0 log — for GitHub code scanning and other SARIF consumers
github GitHub Actions workflow annotations (::error file=…)
gitlab GitLab Code Quality report (JSON array)

--output-file FILE writes the report to a file instead of stdout (for the json, json-indent, sarif, github and gitlab formats) — handy for uploading a CI artifact. A missing target directory yields a clear error rather than a traceback.

Terminal window
robotcode analyze code --output-format sarif --output-file reports/robotcode.sarif

The default format prints one finding per line:

tests/login.robot:4:5: [ERROR] KeywordNotFound: No keyword with name 'Lgin User' found.
tests/login.robot:7:1: [WARNING] KeywordNotUsed: Keyword 'Helper' is not used.
Files: 12, Errors: 1, Warnings: 1, Infos: 0, Hints: 0 (in 0.42s)
  • Each line is path:line:column: [SEVERITY] Code: message; the severity tag is colored (red/yellow/blue/cyan).
  • Output is sorted by (file, line, column), so it's stable regardless of internal analysis order.
  • Workspace-level problems that aren't tied to a single file (e.g. a broken command-line variable file) are prefixed with .:.
  • Related locations are shown on indented -> lines.
  • The summary line is colored after the highest severity present.

By default RobotCode trims the Python traceback and PYTHONPATH listing that Robot Framework appends to import errors. Pass --show-tracebacks to keep the full message.

--output-format sarif emits a SARIF 2.1.0 log — the format GitHub code scanning, Azure DevOps and the VS Code SARIF Viewer consume.

Terminal window
robotcode analyze code --output-format sarif --output-file robotcode.sarif

Paths are written relative to the project root, which is what GitHub code scanning expects (use --full-paths for absolute paths). Once uploaded, an alert stays stable as long as the finding does — an unrelated edit that merely shifts its line number won't create a duplicate.

--output-format github emits workflow annotations. Printed in a GitHub Actions job, they appear inline on the affected lines in the PR diff and in the run summary.

Terminal window
robotcode analyze code --output-format github

Each diagnostic becomes one annotation line:

::error file=tests/login.robot,line=4,endLine=4,col=5,endColumn=14,title=KeywordNotFound::No keyword with name 'Lgin User' found.

--output-format gitlab emits a Code Quality report that GitLab renders in the Merge Request Code Quality widget.

Terminal window
robotcode analyze code --output-format gitlab --output-file gl-code-quality.json

Each diagnostic becomes one entry in the JSON array:

[
{
"description": "No keyword with name 'Lgin User' found.",
"check_name": "KeywordNotFound",
"fingerprint": "…",
"severity": "major",
"location": { "path": "tests/login.robot", "lines": { "begin": 4 } }
}
]

Produce a SARIF file and upload it so the findings appear under Security → Code scanning. The security-events: write permission is required for the upload, and continue-on-error lets the upload run even when analyze code exits non-zero because it found problems.

name: RobotCode Analyze
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write # required for upload-sarif
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.x"
# Set up the project with whatever package manager it uses (pip, uv,
# poetry, pdm, hatch, ...); analysis just needs robotcode[analyze] available.
- run: pip install robotcode[analyze]
- run: robotcode analyze code --output-format sarif --output-file robotcode.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: robotcode.sarif

Print the github format directly — the diagnostics show up as inline annotations on the run and in the PR diff. No upload and no extra permissions needed.

name: RobotCode Analyze
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.x"
# Set up the project with whatever package manager it uses (pip, uv,
# poetry, pdm, hatch, ...); analysis just needs robotcode[analyze] available.
- run: pip install robotcode[analyze]
- run: robotcode analyze code --output-format github

Write the GitLab report and expose it as a codequality artifact; GitLab renders it in the Merge Request Code Quality widget.

robotcode-analyze:
image: python:3
script:
# set up the project with whatever package manager it uses (pip shown here)
- pip install robotcode[analyze]
- robotcode analyze code --output-format gitlab --output-file gl-code-quality.json
artifacts:
reports:
codequality: gl-code-quality.json
Terminal window
# Exit non-zero only for errors; warnings/infos/hints are reported but ignored
robotcode analyze code --exit-code-mask warn,info,hint
Terminal window
robotcode analyze code --output-format json | jq '.summary'

To speed up repeat analysis, RobotCode caches resolved library/variable imports (and, unless you pass --no-cache-namespaces, analyzed namespaces) on disk. Library and variable file imports are cached per set of import arguments, so changing an import's arguments loads it again with the new ones. The analyze cache subcommands let you inspect and clear it:

Terminal window
robotcode analyze cache info # where the cache lives and how big it is
robotcode analyze cache list # what's cached
robotcode analyze cache path # print the cache directory
robotcode analyze cache clear # remove it (e.g. after changing ignored-libraries)
robotcode analyze cache prune # delete the whole cache directory, for every Python/RF version

prune refuses while another process uses the cache; --force deletes it anyway.

The cache lives in a .robotcode_cache folder in the project root. cache-dir in the [tool.robotcode-analyze.cache] section of robot.toml moves it, for analyze and for the language server; for analyze, the ROBOTCODE_CACHE_DIR environment variable takes precedence.

Clearing the cache is the usual fix after changing cache-affecting settings such as ignore-arguments-for-library.

After each run, analyze code removes the entries of Robot Framework files that no longer exist, also of files outside the paths it analyzed. The language server removes them as soon as it notices that a file was deleted. The analysis of a file with a failed import is not cached, so a missing file or package is found once it is there. A cached analysis is not used either when an import of the file now finds another file or none, for example after a change of the Python path, or when an environment variable that the file uses without a default value has been set or unset since.

Everything controllable on the command line (and more) lives under [tool.robotcode-analyze]:

[tool.robotcode-analyze]
exclude-patterns = ["**/generated/**"]
global-library-search-order = ["MyPreferredLibrary"]
load-library-timeout = 30
[tool.robotcode-analyze.code]
collect-unused = true
exit-code-mask = ["warn", "info", "hint"]
[tool.robotcode-analyze.modifiers]
ignore = ["VariableNotFound"]
error = ["MultipleKeywords"]
[tool.robotcode-analyze.cache]
ignored-libraries = ["MyDynamicLibrary"]

A library or variable file that takes longer than load-library-timeout seconds (default 10) to load is stopped, and its import reports the timeout (LibraryTimeoutError or VariablesTimeoutError). An import that ends before it finishes, for example through sys.exit(), reports LibraryExitError or VariablesExitError. RobotCode loads it again when a file in the library's directory, or the variable file itself, changes, or in a new session, for example after the language server restarts or on the next analyze code run.

See the robot.toml configuration reference for the complete list of settings.

--output-format json (or json-indent) emits a single object with two keys, using the same LSP Diagnostic shape as robotcode discover:

{
"diagnostics": {
"tests/login.robot": [
{
"range": { "start": { "line": 3, "character": 4 },
"end": { "line": 3, "character": 13 } },
"message": "No keyword with name 'Lgin User' found.",
"severity": 1,
"code": "KeywordNotFound",
"source": "robotcode"
}
]
},
"summary": { "files": 12, "errors": 1, "warnings": 0, "infos": 0, "hints": 0 }
}

Field notes:

  • Keys in diagnostics are source paths (project-relative POSIX, or absolute with --full-paths). Workspace-level diagnostics key on ..
  • severity follows the LSP enum: 1 = Error, 2 = Warning, 3 = Information, 4 = Hint.
  • Positions are 0-based (LSP convention), unlike SARIF/GitHub which are 1-based.
  • The JSON always carries the full diagnostic message — --show-tracebacks does not apply.
  • summary is always present, even for a clean run (all counts 0), so consumers get a stable schema.

Released under the Apache-2.0. Copyright © 2022-present Daniel Biehl