Firebase-tools: Firestore emulator: Error: 14 UNAVAILABLE

Created on 14 Dec 2019  路  6Comments  路  Source: firebase/firebase-tools

Firebase CLI 7.10.0
Firestore emulator 1.10.2

index.js:

const functions = require ('firebase-functions');
const admin = require ('firebase-admin');
admin.initializeApp();
const firestore = admin.firestore();

exports.helloWorld = functions.https.onRequest (async (req, res) => {
  const docRef = firestore.collection ('col').doc ('doc');
  await docRef.set ({foo: 'bar'});
  const docSnap = await docRef.get();
  const wroteCorrectly = docSnap.get ('foo') == 'bar';
  res.send (wroteCorrectly ? 'Worked' : 'Did not work');
});

Expected: 'Worked'.
Instead: Error: 14 UNAVAILABLE: No connection established (etc.)

  • FIRESTORE_EMULATOR_PORT is set (8080).
  • GOOGLE_APPLICATION_CREDENTIALS are set.
  • This runs fine on the remote Firestore. Only a problem when using local emulator.
firestore question

Most helpful comment

@bwhrsc thanks for your feedback, it shows that some things are not documented as clearly as they could be.

FIRESTORE_EMULATOR_HOST is an environment variable you can set to tell Firebase SDKs to talk to the emulator rather than to production. It does not control where the emulator runs. To set that, you change a value in firebase.json:

{
  "emulators": {
     "firestore": { "port": 8080 }
  }
}

So when you want to run tests against the emulator you can use one of two methods.

Method 1: Manual
In one terminal, run the emulators:

$ firebase emulators:start

In another terminal, run your tests and set the env var:

$ export FIRESTORE_EMULATOR_HOST="localhost:8080"
$ npm run test

Method 2: Automatic
In your terminal, run:

$ firebase emulators:exec "npm run test"

The above command will do the following things, in order:

  1. Start up all the emulators (and wait for them to confirm they are running)
  2. Launch a new subprocess with the proper environment variables (FIRESTORE_EMULATOR_HOST, etc) setup
  3. Run your test command (npm run test or anything else) in that subprocess
  4. Wait for your test command to finish
  5. Cleanly shut down all the emulators and exit

So I believe that should fix your issue. Let me know if something is still not working!

All 6 comments

@bwhrsc I am able to run that exact code using the latest version of the Firebase CLI and get "Worked".

Can you tell me a little more about your setup?

  • What operating system?
  • How are you launching the emulators?
  • How are you invoking the function?
  • Which line of code throws the error? Is it the first await docRef.set() or a different operation?
  • What version of firebase-admin and firebase-functions are you using?

I had this problem for few months. My issue is, the port 8080 is closed on my Mac. I opened the port and everything worked perfectly.

  • macOS 10.14
  • Launching emulators with $ firebase emulators:start.
  • Invoking the function in the browser.
  • ? Cannot reproduce problem now. (I could 20 minutes ago.)
  • firebase-functions 3.3.0, firebase-admin 8.8.0.

I thought I had fixed this by changing FIRESTORE_EMULATOR_HOST to =localhost:8080 instead of =8080. However when I subsequently changed it back to =8080 it continued working. I still have no idea what was happening here.

However, there seems to be another (related?) problem.

Now, although the function works fine when invoked from the browser, it fails when invoked in a test.

./test/index.test.js :

const chai = require ('chai');
const assert = chai.assert;
const expect = chai.expect;
const test = require ('firebase-functions-test') ({projectId: 'bwhogan191212'}, "/Users/benjaminhogan/Documents/Programming/Firebase/Firebase_Tests/bwhogan191212-1131f11f1924.json");
const admin = require ('firebase-admin');
try { admin.initializeApp(); } catch (e) {}
const firestore = admin.firestore();

const app = require ('../index.js');

describe ('My App', () => {

  it ('Returns \'Worked\'', (done) => {
    const req = {};
    const res = {
      send: (msg) => {
        assert.equal (msg, "Worked");
        done();
      }
    };
    app.helloWorld (req, res);
  });

});

test.cleanup();
  • Start emulators with $ FIRESTORE_EMULATOR_HOST=8080 GOOGLE_APPLICATION_CREDENTIALS="path/to/credentials.json" firebase emulators:start
  • Run test with $ mocha --reporter spec.
  • The test fails with a timeout (over 2000ms). Meanwhile, looking in my Firebase console, I can see that data has been written to production Firestore.

NB if you are trying the above test you need to edit my original index.js to use
try { admin.initializeApp(); } catch (e) {}

Ok, seems I have now solved this follow-up issue by using $ export FIRESTORE_EMULATOR_HOST=localhost:8080 in the terminal session that is running the test as well as the one that is running the emulator.
This is kind of counterintuitive to me and something that would have been great to see discussed clearly in the documentation.
PS weirdly, the session running the test seems to require =localhost:8080 rather than =8080, whereas the emulator session can handle either.

EDIT: On reflection, the emulator does tell you i firestore: For testing set FIRESTORE_EMULATOR_HOST=localhost:8080. I just didn't realise that testing meant specifically unit testing in a different session.

@bwhrsc thanks for your feedback, it shows that some things are not documented as clearly as they could be.

FIRESTORE_EMULATOR_HOST is an environment variable you can set to tell Firebase SDKs to talk to the emulator rather than to production. It does not control where the emulator runs. To set that, you change a value in firebase.json:

{
  "emulators": {
     "firestore": { "port": 8080 }
  }
}

So when you want to run tests against the emulator you can use one of two methods.

Method 1: Manual
In one terminal, run the emulators:

$ firebase emulators:start

In another terminal, run your tests and set the env var:

$ export FIRESTORE_EMULATOR_HOST="localhost:8080"
$ npm run test

Method 2: Automatic
In your terminal, run:

$ firebase emulators:exec "npm run test"

The above command will do the following things, in order:

  1. Start up all the emulators (and wait for them to confirm they are running)
  2. Launch a new subprocess with the proper environment variables (FIRESTORE_EMULATOR_HOST, etc) setup
  3. Run your test command (npm run test or anything else) in that subprocess
  4. Wait for your test command to finish
  5. Cleanly shut down all the emulators and exit

So I believe that should fix your issue. Let me know if something is still not working!

Was this page helpful?
0 / 5 - 0 ratings