Skip to content

gax: stub-creation-time auth failure bypasses the API-call promise — application .catch() cannot intercept it (surfaces as uncaughtException) #9085

Description

@murad-mocart

Environment

  • google-gax 4.6.1 (resolved transitively via @google-cloud/logging 11.3.0)
  • google-auth-library (as resolved by the above)
  • Node.js v22.x, Linux/macOS

Summary

When credential resolution fails at stub creation time (e.g. GOOGLE_APPLICATION_CREDENTIALS points at a missing file), the failure does not propagate through the API call's returned promise. An application's .catch() on the API call never fires — instead the error surfaces as a process-level uncaughtException (and with no handler installed, kills the process).

Reproduction

// GOOGLE_APPLICATION_CREDENTIALS=/tmp/definitely-does-not-exist-credentials.json node repro.js
const { Logging } = require('@google-cloud/logging'); // any gax-based client reproduces

process.on('unhandledRejection', (e) => console.log('[unhandledRejection]', e.message));
process.on('uncaughtException', (e) => {
  console.log('[uncaughtException — should have been a promise rejection]', e.message);
  process.exit(0);
});

const log = new Logging().log('repro-log');
const entry = log.entry({ resource: { type: 'global' } }, { message: 'hello' });

log
  .write(entry)
  .then(() => console.log('[then] ok'))
  .catch((e) => console.log('[catch] caught as expected:', e.message)); // ← never runs

Observed output:

[uncaughtException — should have been a promise rejection] The file at /tmp/definitely-does-not-exist-credentials.json does not exist, or it is not a file. ENOENT: no such file or directory ...

Stack points into stub creation:

    at GoogleAuth._getApplicationCredentialsFromFilePath (google-auth-library/build/src/auth/googleauth.js:375)
    ...
    at GrpcClient.createStub (google-gax/build/src/grpc.js:318)

The explicit .catch() on the write promise never fires; unhandledRejection never fires either — the error bypasses the promise chain entirely. (Also reproduced in the client library's own wrapper: @google-cloud/logging's logging_service_v2_client.js has a throw err; in the stub-initialization path that re-throws outside any user-reachable promise.)

Expected

A credential/auth failure during lazy stub creation should reject the pending API-call promise(s), so application code can handle it (.catch(), retry, fallback) like any other API error.

Impact

Any long-running service that treats logging/telemetry as non-critical and wraps every call in try/catch (or .catch()) still crashes on boot-time/lazy-init auth misconfiguration — there is no userland way to intercept it short of a process-wide uncaughtException handler. We currently work around it by issuing a deliberate "pre-warm" write at startup so the failure happens at a controlled fail-fast point.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions