Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

npm:google-auth-library causes unresolved top-level await #27803

Open
dandv opened this issue Jan 24, 2025 · 2 comments
Open

npm:google-auth-library causes unresolved top-level await #27803

dandv opened this issue Jan 24, 2025 · 2 comments

Comments

@dandv
Copy link

dandv commented Jan 24, 2025

Version: Deno 2.1.6

If the Google Auth library fails to lookup googleapis.com, that error is apparently impossible to catch from Deno scripts.

Reproduction

  1. Download the password-protected service account file from the bottom of the first comment of this issue and unzip service-account-key-deno-firestore-dns-bug.json.

  2. Save the following Deno script as deno-fail.ts:

    import { GoogleAuth } from 'npm:google-auth-library';
    
    export async function getOAuthToken() {
        try {
            // Ensure this call succeeds by using the valid service account JSON file extracted earlier
            const auth = new GoogleAuth({
                keyFilename: 'service-account-key-deno-firestore-dns-bug.json',
            });
    
            // Ensure this call succeeds by using the valid service account JSON file extracted earlier
            const client = await auth.getClient();
            console.log('---- Before .getAccessToken() call');
            // await fetch('https://pleasedontregisterthisdomainnam.e');  // this IS caught in both Deno and Node
            await client.getAccessToken();  // this will throw if offline
            console.log('---- execution correctly never reaches this point');
        } catch (e) {
            console.log('🦕🦕🦕🦕 Why does execution not reach this point in Deno but does in Node?', (e as Error).message);
        }
    }
    
    const token = await getOAuthToken();
    
    Deno.test('access token', async () => {
        console.log(token);
    });
  3. Go offline

  4. Run the test:

$ deno test -A deno-fail.ts 
------- pre-test output -------
---- Before .getAccessToken() call
Uncaught error from ./deno-fail.ts FAILED
Uncaught error from ./deno-fail.ts FAILED

 ERRORS 

./deno-fail.ts (uncaught error)
error: Top-level await promise never resolved
const token = await getOAuthToken();
              ^
    at <anonymous> (file:///home/dandv/deno-bugs/deno-fail.ts:23:15)
This error was not caught from a test and caused the test runner to fail on the referenced module.
It most likely originated from a dangling promise, event/timeout handler or top-level code.

 FAILURES 

./deno-fail.ts (uncaught error)

FAILED | 0 passed | 2 failed (0ms)

error: Test failed

Also note that the error reason (getaddrinfo EAI_AGAIN www.googleapis.com) was not output.

Node version

The node version of the script behaves as expected. Here is node-works.ts:

import test from 'node:test';
import { GoogleAuth } from 'google-auth-library';

export async function getOAuthToken() {
    try {
        // Ensure this call succeeds by using the valid service account JSON file extracted earlier
        const auth = new GoogleAuth({
            keyFilename: '../../service-account-key-cfsh.json',
        });

        // Ensure this call succeeds by using the valid service account JSON file extracted earlier
        const client = await auth.getClient();
        console.log('---- Before .getAccessToken() call');
        // await fetch('https://pleasedontregisterthisdomainnam.e');  // this IS caught in both Deno and Node
        await client.getAccessToken();  // this will throw if offline
        console.log('---- execution correctly never reaches this point');
    } catch (e) {
        debugger;
        console.log('🦕🦕🦕🦕 Why does execution not reach this point in Deno but does in Node?', (e as Error).message);
    }
}

const token = await getOAuthToken();

test('access token', async () => {
    console.log(token);
});

When run with node, the script correctly prints the dinos line.

$ node --experimental-strip-types node-works.ts 
(node:1523620) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:1523620) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
---- Before .getAccessToken() call
🦕🦕🦕🦕 Why does execution not reach this point in Deno but does in Node? request to https://www.googleapis.com/oauth2/v4/token failed, reason: getaddrinfo EAI_AGAIN www.googleapis.com
undefined
✔ access token (1.308108ms)
@bartlomieju bartlomieju changed the title catch block not executed with Deno.test, but executed with node test npm:google-auth-library causes unresolved top-level await Jan 24, 2025
@bartlomieju
Copy link
Member

bartlomieju commented Jan 24, 2025

The bug is here:

Top-level await promise never resolved
const token = await getOAuthToken();
              ^
    at <anonymous> (file:///home/dandv/deno-bugs/deno-fail.ts:23:15)

A TLA promise that never resolves causes execution termination (because the JS engine can't make any progress) and it's expected that the catch block is not caught. I updated the title of the issue.

@dandv
Copy link
Author

dandv commented Jan 24, 2025

Thanks for looking into this. If I move the await getOAuthToken(); into the Deno.test() so it's no longer top-level, the catch block still doesn't execute, but at least now I can see the uncaught error:

Deno.test('access token', async () => {
    await getOAuthToken();
});
$ deno test -A deno-fail.ts 
running 1 test from ./deno-fail.ts
access token ...
------- output -------
---- Before .getAccessToken() call
Uncaught error from ./deno-fail.ts FAILED
access token ... cancelled (0ms)

 ERRORS 

./deno-fail.ts (uncaught error)
error: Error: getaddrinfo ENOTFOUND www.googleapis.com
    at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:93:9)
    at __node_internal_ (ext:deno_node/internal/errors.ts:246:10)
    at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:43:26)
This error was not caught from a test and caused the test runner to fail on the referenced module.
It most likely originated from a dangling promise, event/timeout handler or top-level code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants