fsl doctor: check your setup
Most ways a logging setup breaks give no error. fsl doctor reads the project off disk and names what it finds, with no network and no credentials.
Source maps published next to the app, so anyone can read your code. A backend on the wrong Node. Two copies of the same SDK. None of them throws. The logs do not arrive, or they arrive unreadable, and you find out when you need them.
fsl doctor reads the project from disk and reports what it finds. It only reports facts it can read from a file. It does not guess from your source code.
Run it
Section titled “Run it”On a Firebase project it reads firebase.json. Anywhere else, say where things are.
# Firebase: reads firebase.jsonnpx fsl doctor# anything else: say where things arenpx fsl doctor --backend=./server --dist=./distIt prints a summary of how your setup will behave, then any findings:
Setup: Firebase — functions in ./functions, web build in ./dist
Logging firebase-functions write() Trace ids from each Cloud Functions trigger Storage firebase-admin, default bucket Callable createClientLogFunction available
⚠ duplicate-storage 2 copies of @google-cloud/storage (8.2.0 at the top level, 7.22.0 inside firebase-admin) Fix: npm dedupeRun npm install first. Doctor reads node_modules. Without it, a Firebase project reports callable-without-firebase-functions, and a backend with no package.json reports could-not-check.
Findings
Section titled “Findings”| Id | Level | Means |
|---|---|---|
maps-published |
error | .map files are in the folder hosting serves and nothing removes them before a deploy. There is no fsl upload-sourcemaps in a package.json script or hosting predeploy, and no **/*.map in hosting ignore. Your source code is public. |
node-version |
error | The backend’s Node is below 22. |
callable-without-firebase-functions |
error | firebase.json has functions, but firebase-functions is not installed there. |
could-not-check |
error | Doctor could not read something it needed, including “run npm install first”. |
duplicate-storage |
warning | Two copies of @google-cloud/storage. Fix with npm dedupe. |
unsupported-peer |
warning | An installed peer (firebase-admin, firebase-functions, firebase) is outside the supported range. |
logs-inside-functions-source |
warning | Local log files sit inside the Functions source folder and firebase.json → ignore does not cover them. The emulator restarts on every entry, and a deploy uploads them. |
skill-out-of-date |
warning | A skill in .claude/skills/ has an fsl-version that differs from the fsl running doctor. Run npx fsl install-skills. A skill with no fsl-version is not ours and is skipped. See Skills for coding agents. |
embedded-maps-without-release |
warning | Maps are embedded without a .release marker, so an older release can resolve against the wrong map. |
Each finding prints a one-line Fix:. The ids and fields are part of the 1.0 API, so scripts may depend on them.
Use it in CI
Section titled “Use it in CI”npx fsl doctor --strict --json| Flag | Effect |
|---|---|
--strict |
Fail on warnings as well as errors. |
--json |
Print the result as JSON instead of the report. |
--backend=<path> |
The backend folder, when there is no firebase.json to read. |
--dist=<path> |
The web build folder, when there is no firebase.json to read. |
The exit code is 0 when there are no errors and 1 when there is one. Without --strict, a deliberate choice such as running with no Storage does not break CI. A check doctor could not run is always an error, never a pass.
JSON output
Section titled “JSON output”{ "setup": { "kind": "firebase", "backend": "./functions", "dist": "./dist", "logging": "firebase-functions", "trace": "trigger", "storage": "firebase-admin", "callable": true }, "findings": [ { "id": "duplicate-storage", "level": "warning", "message": "2 copies of @google-cloud/storage (8.2.0 at the top level, 7.22.0 inside firebase-admin)", "fix": "npm dedupe" } ], "exitCode": 0}| Field | Values |
|---|---|
setup.kind |
firebase, node, browser-only, backend-only |
setup.logging |
firebase-functions, stdout, none |
setup.trace |
trigger, header, none |
setup.storage |
firebase-admin, google-cloud-storage, none |
google-cloud-storage still needs a bucket named in your code, and doctor cannot see it.
What it does not check
Section titled “What it does not check”Doctor does not check anything set in your own code: the release id you pass to initLogger, or a bucket name passed to a handler. Its Storage line says so, “needs a bucket named in code (not checked)”, rather than guessing. To check those, send a real entry with sendTestLog() as described in Source maps.
When something is wrong and doctor is clean, see Troubleshooting.
Made by Dasasian