googleapis / googleapis/google-cloud-node

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

Open
#9,085 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
3.2k
Forks
712
Avg merge
2d 3h
Merged PRs (30d)
99

Description

## 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

```js
// 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.

Contributor guide

Open the contributing guide

Research direction

Start with the reproduction and trace lazy stub creation through google-gax/build/src/grpc.js at GrpcClient.createStub, then inspect logging_service_v2_client.js for the stub-initialization throw. Verify that a credential failure from the missing-file setup reaches the pending write promise and is handled by its .catch(), rather than surfacing as uncaughtException.

Written by the indexing model from the issue text.

Assessment

Tech stack
google-cloud, node.js, typescript
Domain
api, authentication
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.