firebase-tools: [Firebase functions emulator] Introducing a runtime error in the top level scope crashes the emulator.

[REQUIRED] Environment info

firebase-tools: 11.0.1

Platform: Windows

[REQUIRED] Test case

Any javascript runtime error in the global scope (that is, outside the body of any cloud function definition) crashes the emulator.

[REQUIRED] Steps to reproduce

  1. Run firebase init in an empty directory.
  2. Select only functions emulator, javascript (also happens with typescript), no eslint (also happens with eslint on), no default project (also happens when a project is selected).
  3. Run firebase emulators:start --only functions
  4. Open the automatically generated functions/index.js file and type something that results in a syntax error. E.g.:
const functions = require("firebase-functions");

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//   functions.logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });
thisIsNotDefined();
  1. Observe how the emulator crashes with Error: Failed to load function definition from source: Failed to generate manifest from function source: ReferenceError: thisIsNotDefined is not defined

[REQUIRED] Expected behavior

The emulator does not crash. It should issue a warning that it could not load the function code, but it should not crash, thereby also leaving other dangling processes running on the machine.

[REQUIRED] Actual behavior

Here the log of the firebase init command.

firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##      
     ######    ##  ########  ######   ########  #########  ######  ######  
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##      
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  C:\dev\reprojs

Before we get started, keep in mind:

  * You are currently outside your home directory

? Are you ready to proceed? Yes
? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. Functions: Configure a Cloud Functions directory and its files

=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.

? Please select an option: Don't set up a default project

=== Functions Setup

A functions directory will be created in your project with sample code
pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions? JavaScript  
? Do you want to use ESLint to catch probable bugs and enforce style? No
+  Wrote functions/package.json
+  Wrote functions/index.js
+  Wrote functions/.gitignore
? Do you want to install dependencies with npm now? Yes

added 238 packages, and audited 239 packages in 13s

15 packages are looking for funding
  run `npm fund` for details

3 high severity vulnerabilities

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...
i  Writing gitignore file to .gitignore...

+  Firebase initialization complete!

And here the command that starts the emulator.

$ firebase emulators:start --only functions --debug
[2022-06-03T13:34:09.368Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
[2022-06-03T13:34:09.370Z] > authorizing via signed-in user (info@aestico.ch)
i  emulators: Starting emulators: functions {"metadata":{"emulator":{"name":"hub"},"message":"Starting emulators: functions"}}
[2022-06-03T13:34:09.386Z] [hub] writing locator at C:\Users\ggg94\AppData\Local\Temp\hub-testenvironmentaestico.json
!  functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: auth, firestore, database, hosting, pubsub, storage {"metadata":{"emulator":{"name":"functions"},"message":"The following emulators are not running, calls to these services from the Functions emulator will affect production: \u001b[1mauth, firestore, database, hosting, pubsub, storage\u001b[22m"}}       
+  functions: Using node@16 from host. {"metadata":{"emulator":{"name":"functions"},"message":"Using node@16 from host."}}
[2022-06-03T13:34:09.418Z] defaultcredentials: writing to file C:\Users\ggg94\AppData\Roaming\firebase\info_aestico_ch_application_default_credentials.json
[2022-06-03T13:34:09.420Z] Setting GAC to C:\Users\ggg94\AppData\Roaming\firebase\info_aestico_ch_application_default_credentials.json {"metadata":{"emulator":{"name":"functions"},"message":"Setting GAC to C:\\Users\\ggg94\\AppData\\Roaming\\firebase\\info_aestico_ch_application_default_credentials.json"}}
[2022-06-03T13:34:09.424Z] >>> [apiv2][query] GET https://firebase.googleapis.com/v1beta1/projects/testenvironmentaestico/adminSdkConfig [none]
[2022-06-03T13:34:09.716Z] <<< [apiv2][status] GET https://firebase.googleapis.com/v1beta1/projects/testenvironmentaestico/adminSdkConfig 200
[2022-06-03T13:34:09.717Z] <<< [apiv2][body] GET https://firebase.googleapis.com/v1beta1/projects/testenvironmentaestico/adminSdkConfig {"projectId":"testenvironmentaestico","databaseURL":"https://testenvironmentaestico-default-rtdb.europe-west1.firebasedatabase.app","storageBucket":"testenvironmentaestico.appspot.com","locationId":"europe-west6"}
[2022-06-03T13:34:09.740Z] Ignoring unsupported arg: auto_download {"metadata":{"emulator":{"name":"ui"},"message":"Ignoring unsupported arg: auto_download"}}
[2022-06-03T13:34:09.741Z] Ignoring unsupported arg: port {"metadata":{"emulator":{"name":"ui"},"message":"Ignoring unsupported arg: port"}}
[2022-06-03T13:34:09.741Z] Starting Emulator UI with command {"binary":"node","args":["--dns-result-order=ipv4first","C:\\Users\\ggg94\\.cache\\firebase\\emulators\\ui-v1.7.0\\server.bundle.js"],"optionalArgs":[],"joinArgs":false} {"metadata":{"emulator":{"name":"ui"},"message":"Starting Emulator UI with command {\"binary\":\"node\",\"args\":[\"--dns-result-order=ipv4first\",\"C:\\\\Users\\\\ggg94\\\\.cache\\\\firebase\\\\emulators\\\\ui-v1.7.0\\\\server.bundle.js\"],\"optionalArgs\":[],\"joinArgs\":false}"}}
i  ui: Emulator UI logging to ui-debug.log {"metadata":{"emulator":{"name":"ui"},"message":"Emulator UI logging to \u001b[1mui-debug.log\u001b[22m"}}
[2022-06-03T13:34:09.875Z] Web / API server started at localhost:4000
 {"metadata":{"emulator":{"name":"ui"},"message":"Web / API server started at localhost:4000\n"}}
i  functions: Watching "C:\dev\reprojs\functions" for Cloud Functions... {"metadata":{"emulator":{"name":"functions"},"message":"Watching \"C:\\dev\\reprojs\\functions\" for Cloud Functions..."}}
[2022-06-03T13:34:10.020Z] Validating nodejs source
[2022-06-03T13:34:11.520Z] > [functions] package.json contents: {
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "16"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^10.0.2",
    "firebase-functions": "^3.18.0"
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.0"
  },
  "private": true
}
[2022-06-03T13:34:11.524Z] Building nodejs source
[2022-06-03T13:34:11.525Z] Analyzing nodejs backend spec
[2022-06-03T13:34:11.529Z] Could not find functions.yaml. Must use http discovery
[2022-06-03T13:34:11.740Z] Serving at port 9005

[2022-06-03T13:34:11.813Z] Got response from /__/functions.yaml {"endpoints":{},"specVersion":"v1alpha1","requiredAPIs":[]}
[2022-06-03T13:34:11.819Z] shutdown requested via /__/quitquitquit


┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator  │ Host:Port      │ View in Emulator UI             │
├───────────┼────────────────┼─────────────────────────────────┤
│ Functions │ localhost:5001 │ http://localhost:4000/functions │
└───────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

[2022-06-03T13:34:19.899Z] File C:\dev\reprojs\functions\index.js changed, reloading triggers {"metadata":{"emulator":{"name":"functions"},"message":"File C:\\dev\\reprojs\\functions\\index.js changed, reloading triggers"}}
[2022-06-03T13:34:20.906Z] Validating nodejs source
[2022-06-03T13:34:22.387Z] > [functions] package.json contents: {
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "16"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^10.0.2",
    "firebase-functions": "^3.18.0"
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.0"
  },
  "private": true
}
[2022-06-03T13:34:22.391Z] Building nodejs source
[2022-06-03T13:34:22.392Z] Analyzing nodejs backend spec
[2022-06-03T13:34:22.393Z] Could not find functions.yaml. Must use http discovery
[2022-06-03T13:34:22.608Z] Serving at port 9005

[2022-06-03T13:34:22.677Z] Got response from /__/functions.yaml Failed to generate manifest from function source: ReferenceError: thisIsNotDefined is not defined
[2022-06-03T13:34:22.680Z] Failed to parse functions.yamlincomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line at line 1, column 65:
     ...  function source: ReferenceError: thisIsNotDefined is not defined
                                         ^ {"name":"YAMLException","reason":"incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line","mark":{"name":null,"buffer":"Failed to generate manifest from function source: ReferenceError: thisIsNotDefined is not defined\n\u0000","position":64,"line":0,"column":64},"message":"incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line at line 1, column 65:\n     ...  function source: ReferenceError: thisIsNotDefined is not defined\n                                         ^"}
[2022-06-03T13:34:22.686Z] shutdown requested via /__/quitquitquit


Error: Failed to load function definition from source: Failed to generate manifest from function source: ReferenceError: thisIsNotDefined is not defined

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 18 (5 by maintainers)

Commits related to this issue

Most upvoted comments

We’ve see a number of concerns here.

  1. There’s a request that we can handle errors thrown from global scope. This would be interesting, but it cannot be done. We’re calling require on your code and it throws an Exception. That means require doesn’t return a module and we have no handle to your functions for us to route traffic to you.

I think you’re misunderstanding here. It’s not that top level errors should somehow magically be made not broken. It’s about not taking down the entire system (really uncleanly as well, as others have mentioned - can’t even start it back up without manually killing java processes hogging the ports).

All it would require is… well surely nothing but a catch? If it throws, completely ignore it as far as state goes. Spew an error and otherwise pretend it never happened.

Every other hot reload software of all kinds have figured out how to do it, maybe due to my limited understanding but I don’t get from your explanation why this would differ.

Why is the handle gone for good? And if entire env does indeed go toast and is impossible to retain when this happens why not simply reinitialize? Anything that doesn’t cause a crash and involves a bunch of time killing processes so you can start the software again…

I was trying to quickly write a cloud function in Clojurescript and probably managed to crash everything 40 times while working out the kinks since being used to Figwheel and similar :w the document very often out of habit, before I at least managed to get a repl up and running and would only crash occasionally. Extreme example I’m sure, but incredibly frustrating!

i have the same problem 😕 tried to change many things, downgrading, but nothing helps so far

We’ve see a number of concerns here.

  1. There’s a request that we can handle errors thrown from global scope. This would be interesting, but it cannot be done. We’re calling require on your code and it throws an Exception. That means require doesn’t return a module and we have no handle to your functions for us to route traffic to you.
  2. Discussions about proccess.env.FOO not being available at discovery. We’ve got an extra feature coming soon that will let you have dependencies on variables during discovery (e.g. const foo = params.defineString("FOO"))
  3. Some of you are getting strange errors from our new discovery process. This is concerning. I personally write all of my code in TypeScript, so I’m not sure if that’s the issue. Do you have tsc in your functions.beforeDeploy field in firebase.json? I don’t know what the issue is yet; can you help me debug a bit more?

I’d love to know what CLI and SDK version you’re using. I’d like to see what your function definitions look like (not the business logic, just the onRequest(options, callback) bits. And I’d like to see what our discovery process is generating. In your functions directory run FUNCTIONS_CONTROL_API=true PORT=8080 ./node_modules/.bin/firebase-functions .. Then from another terminal run curl -X GET http://localhost:8080/__/functions.yaml.

While experiencing some other bugs, I noticed that this issue came back.

If the functions emulator can’t discover the build, it crashes the process, but leaves the firestore emulator and the emulator ui running and blocking the ports.

To add to @tolgraven’s comment: things get worse when running emulators for Auth, Firestore etc as well, with local persistence; because now you don’t have a way to clean-shutdown the orphaned Auth/Firestore process, and if you kill it (which you’d have to do anyways, at some point), you’ll lose all Firestore data/changes from that session. (Even worse, in some cases, the local persistence store actually ends up corrupted - losing all your local application data, unless you have a previously-saved backup of the persistence directory.)

FWIW I’m currently running with node_modules/firebase-tools/lib/deploy/functions/runtimes/node/triggerParser.js patched (basically by commenting out all the process.send(..) calls), to prevent the Functions emulator from signaling back the code-loading error to the main/parent/controller process. This does prevent the “crash”, but doesn’t give any indication that code-loading actually failed either; logging the error/warning directly from the emulator code, didn’t work (possibly because it runs as a child process; I couldn’t dig deeper into the emulator architecture yet). Anyways, the emulator will keep on trying to re-load the code after every change detection, and eventually deploy the latest version once the load/parse error gets resolved.

As @tolgraven also mentioned, we love the emulator suite (esp. me, who hates needing to have a working/persistent internet connection during dev work); the emulator is our “cloud”, and we’re just saying that it would be perfect if outage of one service won’t bring the whole “cloud” down.