Announced on 31 August 2026, the symfony lsp:check command replays inside continuous integration the diagnostics that were previously confined to the code editor. It catches errors that slip past classic static analysis, such as a misspelled route name or a missing template, by actually booting the application. Shipped with the Symfony 5.20 CLI, it targets teams as much as the coding agents tasked with validating their own changes before review.
The problem: strings that mean something to Symfony
A Symfony application is studded with meaningful strings: route names, template paths, translation keys, service identifiers, configuration keys. To PHP, they are strings like any others, validated by no type system.
“A Symfony application is full of strings that mean something. To PHP, they are strings like any others.”
The result: a route renamed but forgotten in a controller, a missing translation key or a deleted template all pass compilation and type checks, then break at runtime. That is exactly the blind spot the command targets.
What the command checks
The checker covers 30 diagnostic codes grouped by domain. The full list is shown with symfony lsp:check --list-codes.
| Domain | Example detections |
|---|---|
| Routes | Unknown route, missing required parameter |
| Templates & Twig | Missing template, unknown component or filter argument |
| Translations | Missing key, domain or message placeholder |
| Services & configuration | Unknown service or parameter, invalid bundle key or type |
| Messenger | Unknown bus or transport, invalid handler signature |
| Security & forms | Unknown firewall or user provider, invalid form option |
Runtime analysis and CI-ready output
Runtime analysis is on by default: the checker boots the application in the chosen Symfony environment and loads the same metadata as the editor integrations, so diagnostics reflect the real application rather than a guess based on conventions. For CI jobs that must not execute any application code, the --source-only mode is available.
# Check the whole project
symfony lsp:check
# Narrow to directories or patterns
symfony lsp:check src/ templates/
symfony lsp:check 'config/**/*.yaml'
# Without executing application code
symfony lsp:check --source-onlyThree structured formats ease integration: JSON, GitHub Actions annotations and SARIF 2.1 for code-scanning systems. Exit codes tell apart the absence of blocking diagnostics (0), the presence of blocking diagnostics (10), an invalid invocation (11) and an incomplete analysis (12).
# .github/workflows/symfony-diagnostics.yaml
name: Symfony diagnostics
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
tools: symfony-cli
- run: composer install --no-progress
- run: symfony lsp:check --format=githubGradual adoption: baseline and blocking policy
A baseline lets the command land on an existing project without blocking the pipeline right away. Diagnostics already present become non-blocking while remaining visible, and they survive unrelated line movement.
# Generate the baseline
symfony lsp:check --generate-baseline
# Reuse it afterwards
symfony lsp:check --baseline=.symfony-lsp-baseline.json
# Block only on specific codes
symfony lsp:check --fail-on=route.not_found,translation.not_foundWatch out. In its default mode, the command boots the application inside CI: it therefore needs the environment variables and services expected at startup. On a restricted environment, --source-only avoids execution but reduces the depth of the analysis. On an indexing failure or a timeout, the report is marked incomplete rather than silently degraded.
The takeaway
The symfony lsp:check command carries into CI a class of errors previously caught at runtime or in production. By booting the application, it validates the Symfony-specific links that neither typing nor static analysis covers. SARIF and GitHub output, baselines and the source-only mode make it a pipeline-ready tool, including pipelines driven by coding agents.
On my Symfony projects, the nastiest errors are almost never type errors: they are half-renamed routes and phantom translation keys, invisible until the client demo. A command that catches them in CI answers a real need, and I will wire it into at least one pipeline this week. My only reflex of caution: start with a baseline on older projects, or the CI turns red on the very first run. — Simon Janvier
Further reading
Introducing symfony lsp:check: Symfony-Aware Diagnostics in Your CI (Symfony Blog)
