Skip to content

Troubleshooting

Most failures here are silent, so this page starts from the symptom.

Run npx fsl doctor before anything else. It finds the setup problems that can be read from a file, and the rest of this page covers what it cannot. See fsl doctor.

The stack says app-4f2a.js:1:98432 where you expect Checkout.tsx:42:9. Three causes, most common first.

  • The maps were never uploaded, or were uploaded under a different release. The releaseId in initLogger has to equal the value used at upload. Use one variable, set once in the deploy script.
  • The bucket or prefix differs between the upload and the function. They are two halves of one contract and nothing checks one against the other. Pass the same --bucket and --prefix to the upload, and sourceMaps: { bucket, prefix } to the log function. A mismatch looks identical to never having uploaded. The function warns once per release when it resolves nothing, naming the exact object it looked for.
  • There is no Storage at all. With --embed-sourcemaps and no bucket, only the deployed release resolves. Errors from an older release stay minified.

Send sendTestLog() after a deploy and look at the stack on labels.errorType="fsl-verify". See Source maps.

A .map file in the folder hosting serves is your source code, downloadable by anyone. Uploading the maps is one half. Deleting them from the build output is the other half, and it is easy to miss.

fsl upload-sourcemaps deletes them from dist/, so it has to run between the build and the deploy. fsl doctor reports the case as maps-published and exits 1. Put it in CI. See Source maps.

A filter such as labels.appId="my-app" returns nothing, though the entries are there. Labels have to be emitted under logging.googleapis.com/labels. Anywhere else they sit inside the payload, the entries look correct, and they cannot be filtered. Only a deployed run shows it.

fsl writes them in the right place. If you see this, check that the entries come from fsl and not from a console.log that prints JSON, and that the label keys match what npx fsl logs schema lists. See Labels.

On a warm instance, a request’s userId can outlive the request, and the next handler inherits whichever user came before. That happens when request context is set with AsyncLocalStorage.enterWith(), which never unwinds.

withLogging scopes the user to the request, so wrap each handler in it rather than writing context yourself. See Cloud Functions. On the client, logger.setUser(uid) lasts until logger.clearUser(), so call clearUser() on sign-out.

An entry shows as plain text with no severity, no labels, and nothing for Error Reporting to group. Cloud Functions and Cloud Run cut a stdout or stderr line at exactly 102,400 bytes (100 KiB). Past that, the entry does not arrive shortened but valid.

The backend logger shortens any entry over 90 KiB before writing it: breadcrumb data first, then other context, then the tail of a long stack, then long text fields. severity, labels, the trace and serviceContext are never touched. Look for labels.truncated="true". The full copy is fsl-overflow.json in Cloud Storage when a bucket is available. Without one, the original is lost and the process warns once.

If you still see broken text, the entry came from something other than the fsl backend logger. See Screenshots and files.

Work down this list. Each gate drops entries without an error.

Check What to look at
Severity floor In production both the client’s and the function’s minSeverity default to WARNING. An INFO has two places to vanish, leaving the browser and entering Cloud Logging. Set minSeverity on both.
NODE_ENV A define: { 'process.env': {} } in vite.config makes NODE_ENV undefined, and the client floor becomes DEBUG in production. Pass minSeverity explicitly.
Log limit Console lines starting [fsl] Log limit reached or only errors can use the reserved logs. A tab sends a burst of 50, then one log per 60 seconds.
Duplicates [fsl] Duplicate counted for the next summary. After 3 full copies, an error is counted and sent as a summary.
Function concurrency createClientLogFunction runs with maxInstances: 1. Raise it if client logs drop under load, and watch the Cloud Logging bill.
Setup npx fsl doctor for node-version and callable-without-firebase-functions.

If there is no [fsl] warning in the browser console, it was a floor, not a limit. See Rate limits and missing logs.

For a backend that is not Cloud Functions, also check the createHttpLogHandler response codes: 204 written, 400 malformed payload, 401 the authorize gate refused, 405 not a POST, 500 something else. Two cases are common:

  • The browser console shows a CORS error. The route does not answer the OPTIONS request that a browser sends first. In Express, mount the handler with app.all, not app.post.
  • Every log from a visitor gets 401. A gate that checks a Firebase ID token refuses a person who is not signed in, so errors on the sign-in page are lost.

See Cloud Run.

An entry arrives as an error that you did not log as one

Section titled “An entry arrives as an error that you did not log as one”

The backend was given a severity it does not know, for example a level name from another logging library. Cloud Logging has a fixed list. An unknown value used to throw inside the write, and the entry was lost with nothing to show it. fsl writes the entry as ERROR and warns once for each bad value: [fsl] Unknown severity "…" — writing as ERROR. Use one of DEBUG, INFO, NOTICE, WARNING or ERROR.

fsl upload-sourcemaps exits with code 3 when it embedded the maps and then could not upload them to Cloud Storage. A deploy script joined with && stops there.

The usual cause is a project with no Storage and a bucket name in .env.local. The tool reads VITE_FIREBASE_STORAGE_BUCKET and FIREBASE_STORAGE_BUCKET from that file, so it tries the upload although you passed no --bucket. Pass an empty --bucket= to skip the upload. See Source maps.

initLogger<MyAppLabels> or withLogging<MyAppLabels> fails with “Index signature for type ‘string’ is missing in type ‘MyAppLabels’”. The label type is declared as an interface. Declare it as a type. See Labels and filters.

A stack that names a bundle which still exists can resolve against the current release’s map, and give line numbers that are wrong. No error tells you. Keep the release id tied to the commit, and run the upload on every deploy so each release has its own maps. See Source maps.

Local log files inside the Functions source folder make the emulator restart on every entry. Each restart rotates the file, so entries are deleted within seconds. fsl doctor reports logs-inside-functions-source. Point logLocalDir outside the folder, for example '../.fsl-logs'. See Local development.

A repeating error looks like it happened three times

Section titled “A repeating error looks like it happened three times”

Three full copies are sent, then one summary counts the rest. Counting entries gives the wrong number. Take the entry’s labels.repeatKey and run npx fsl logs --repeats <repeatKey>, which ends with the true count. See fsl logs.

Made by Dasasian