Skip to content

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.

On a Firebase project it reads firebase.json. Anywhere else, say where things are.

Terminal window
# Firebase: reads firebase.json
npx fsl doctor
# anything else: say where things are
npx fsl doctor --backend=./server --dist=./dist

It 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 dedupe

Run 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.

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.

Terminal window
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.

{
"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.

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