Debugging and reload on save
Firenook reloads your functions when you save, can hold a call at a breakpoint, and keeps every call’s logs and errors. This page shows how to use each while you work on a function.
Reload on save
Section titled “Reload on save”Firenook watches each codebase’s folder. When you save a file, it waits until a second passes with no further change, then loads that codebase again in a new worker. The terminal says so:
19:47:46 ↻ reloaded 229 ms 3 functions · defaultdefault is the codebase. The watcher leaves out files and folders whose
names start with a dot (so .env files too), node_modules, venv,
*.log files, and the patterns in the codebase’s ignore list in
firebase.json.
A reload that fails keeps the version that loaded before, so your app keeps working while you fix the error:
19:47:49 ✕ reload failed 121 ms default │ SyntaxError: Unexpected token ';' functions/index.js:20 │ exports.oops = (; │ ^ │ still running the version loaded at 19:47:46 · open in consoleSave the fix and the next reload takes over.
For TypeScript, Firenook runs the compiled JavaScript your package.json
points at. Start with --build and Firenook runs your compiler in watch mode
beside the emulators, so every save is compiled and reloaded;
TypeScript functions has the details. If your
sources change but the compiled files do not, the console’s page for the
codebase points it out.
If a save seems to change nothing
Section titled “If a save seems to change nothing”In 0.2.0, a save that stops a codebase loading, such as code that throws
while it loads or a param with no default and no value, is reported only on
the terminal screen. The plain output (--plain, pipes and CI) and the
--debug log print nothing about it, and the version loaded before keeps
answering, so a call still gets the old behaviour. The screen shows it:
19:36:17 ✕ reload failed 117 ms default │ Functions codebase could not be analyzed successfully. It may have a syntax or runtime │ error │ Error: SHOP_REGION is not set │ still running the version loaded at 19:35:59 · open in consoleWhen you run with --plain, open the codebase in the console’s Functions
page after a save: it says the codebase failed to load, with the error, and
that the previous version is still serving. Calling the function you changed
shows the same thing.
Reload by hand
Section titled “Reload by hand”Press r in the terminal to reload every codebase of yours, or use
Reload on a codebase in the console. A reload by
hand is how a change to a .env file or .secret.local reaches your
functions, since Firenook does not reload on those
(environment variables and secrets).
Attach a debugger
Section titled “Attach a debugger”Start with --inspect-functions to open Node’s inspector on each codebase’s
worker:
npx firenook emulators:start --inspect-functionsThe first worker listens on 127.0.0.1:9229, the next on 9230, and so on.
To choose the first port, give it: --inspect-functions=9333. With one
codebase, the plain log names its port:
firenook functions: Using debug port 9229 for functions codebase default.firenook functions: Debugger listening on ws://127.0.0.1:9229/27e0016a-a53d-4dc1-8fa2-1d8dbc90e235Extensions take the first ports, then your codebases. So with an Extension
in firebase.json, the port you give goes to the Extension, and your code
gets the next one:
firenook functions: Using debug port 9229 for functions codebase myext.firenook functions: Using debug port 9230 for functions codebase default. You may need to add manually add this port to your inspector.firenook functions: Debugger listening on ws://127.0.0.1:9230/fbe26b0c-0897-4cef-a4f0-de7742ee7ef8firenook extensions[myext]: Debugger listening on ws://127.0.0.1:9229/0b80ac38-04f5-4411-a598-d0bba6e30439Attach your debugger to the port the log gives default (or your
codebase’s name), here 9230.
While the debugger is on, calls have no timeout, so a breakpoint can hold
one for as long as you need. Without it, a call stops at the function’s
timeoutSeconds.
A reload starts a new worker, and with it a new debugger session on the same port. After a reload, the new worker starts at the next call. Use a debugger that attaches again by itself.
Chrome DevTools
Section titled “Chrome DevTools”-
Start the emulators with
--inspect-functions. -
Open
chrome://inspectin Chrome. Under Devices, choose Configure and check that127.0.0.1:9229(or your port) is in the list. -
Choose Open dedicated DevTools for Node. That window attaches to the worker and attaches again after each reload.
-
In Sources, open your function’s file, set a breakpoint, and call the function from your app or with
npx firenook functions:invoke.
VS Code
Section titled “VS Code”Add an attach configuration, start the emulators with
--inspect-functions, then run it from the Run and Debug view:
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "attach", "name": "Attach to Firenook functions", "address": "127.0.0.1", "port": 9229, "restart": true, "skipFiles": ["<node_internals>/**"] } ]}"restart": true makes VS Code attach again after every reload. With several
codebases, add one configuration per port.
Function logs
Section titled “Function logs”In the terminal
Section titled “In the terminal”Each call is a line: the time, a tick or a cross, the function, how long it took and what called it. The lines your function logged follow it, with structured fields after the message:
19:47:43 ✓ helloWorld 7 ms http · GET / · 200 │ Hello logs! structuredData true 19:47:43 ✕ brokenCheckout 11 ms http · POST / · 500 │ checking out order null │ Unhandled error TypeError: Cannot read properties of null (reading 'items') │ at functions/index.js:12:31 │ + 9 more lines · open in consoleA failed call shows the error and the first line of the stack that is in your own code. open in console leads to the whole call.
With --plain, or when the output goes to a pipe or CI, Firenook prints the
official emulator’s lines instead (the stack trimmed):
firenook functions: Beginning execution of "us-central1-brokenCheckout"firenook functions: {"order":null,"severity":"INFO","message":"checking out"}firenook functions: {"severity":"ERROR","message":"Unhandled error TypeError: Cannot read properties of null (reading 'items')\n at ~/demo-shop/functions/index.js:12:31\n ..."}firenook functions: Finished "us-central1-brokenCheckout" in 12msIn the console
Section titled “In the console”Functions › Invocations in the console lists every call, live. Open one to see how it ended and why, its timing, the request or event it got, the response, its own log lines and the full stack of an error. Replay sends the same request again to the code loaded now; Edit and replay lets you change it first. Firenook keeps the last 2,000 calls.
With –debug
Section titled “With –debug”--debug writes Firenook’s whole log, with times, to
.firenook/runs/session-*/firenook-debug.log. In plain mode the start
prints the file’s path:
Debug log: ~/demo-shop/.firenook/runs/session-HRKIKS/firenook-debug.log[INFO] [2026-10-22T11:48:02.206861Z] [functions] functions: Beginning execution of "us-central1-brokenCheckout"[INFO] [2026-10-22T11:48:02.216076Z] [functions] functions: {"order":null,"severity":"INFO","message":"checking out"}[INFO] [2026-10-22T11:48:02.219083Z] [functions] functions: Finished "us-central1-brokenCheckout" in 12msAttach this file when you report a problem.
Read a failed call
Section titled “Read a failed call”Start with the terminal: the error’s message and the line in your code that threw. For the rest, open the call in the console. There you see the whole stack, the request that caused it and the response your app got, and after a fix you can replay the same request.
A call that runs past its timeoutSeconds is stopped, and the caller gets a
500:
firenook functions[us-central1-quote]: Your function timed out after ~2s. To configure this timeout, see https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.See everything the terminal shows in the terminal,
or trigger any function by hand with functions:invoke, described with
Cloud Functions.