next-auth: Neo4j session error (GetSessionAndUserError)

Adapter type

@next-auth/neo4j-adapter

Environment

System: OS: macOS 13.0 CPU: (16) x64 Intel® Core™ i9-9880H CPU @ 2.30GHz Memory: 317.06 MB / 16.00 GB Shell: 3.2.57 - /bin/bash

Binaries: Node: 18.12.1 - /usr/local/opt/node@18/bin/node Yarn: 1.22.11 - /usr/local/bin/yarn npm: 8.19.2 - /usr/local/opt/node@18/bin/npm

Browsers: Chrome: 107.0.5304.110 Firefox: 101.0 Safari: 16.1

npmPackages: next: 13.0.4 => 13.0.4 next-auth: ^4.16.4 => 4.16.4 react: 18.2.0 => 18.2.0 neo4j-driver: ^5.2.0 swr: ^1.3.0 typescript: 4.9.3

npmPackages: @next-auth/neo4j-adapter: ^1.0.5 => 1.0.5

Reproduction URL

https://github.com/vladutilie/issue-next-auth-neo4j

Describe the issue

Hello,

Vlad here, a Next.js and awesome-packages-related fan, enthusiast 😎 and hopefully a contributor to this community.

Since I’ve started to use Neo4j as Adapter for my NextAuth implementation, I got this error in logs and the client cannot get API responses if the focus of the page is lost. I’m using SWR to call an API endpoint which is using the unstable_getServerSession function to check if the user is authenticated.

Long story short

🍀 I start the server, I open the page (let’s say I’m authenticated) and all data from my API (fetched with SWR) is in place. I browse my page and so on, and all is perfect.

🍎 The issue occurs when I click on another tab (or just I open a new one), browse there for some seconds, and then come back to my initial tab - this will trigger SWR to revalidate (re-fetch) the date. The SWR fetch fails because the unstable_getServerSession function returns null 🤷🏻‍♂️ and at the same time I get the following error.

The error I got

The error I got in VS code logs is the following:

[next-auth][error][adapter_error_getSessionAndUser] 
https://next-auth.js.org/errors#adapter_error_getsessionanduser You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session. {
  message: 'You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session.',
  stack: 'Neo4jError: You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session.\n' +
    '    at new Neo4jError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:77:16)\n' +
    '    at newError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:113:12)\n' +
    '    at Session._beginTransaction (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:280:40)\n' +
    '    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:394:77\n' +
    '    at TransactionExecutor.<anonymous> (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:116:46)\n' +
    '    at step (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:52:23)\n' +
    '    at Object.next (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:33:53)\n' +
    '    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:27:71\n' +
    '    at new Promise (<anonymous>)\n' +
    '    at __awaiter (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:23:12)',
  name: 'Neo4jError'
}
[next-auth][error][SESSION_ERROR] 
https://next-auth.js.org/errors#session_error You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session. Neo4jError: You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session.
    at new Neo4jError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:77:16)
    at newError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:113:12)
    at Session._beginTransaction (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:280:40)
    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:394:77
    at TransactionExecutor.<anonymous> (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:116:46)
    at step (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:52:23)
    at Object.next (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:33:53)
    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:27:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:23:12) {
  name: 'GetSessionAndUserError',
  code: 'N/A'
}

What I have tried?

I have tried to call session.close() in node_modules/@next-auth/neo4j-adapter/dist/utils.js like below, hoping for an eventually fix, but it was in vain:

function client(session) {
    return {
        /** Reads values from the database */
        async read(statement, values) {
            var _a, _b;
            const result = await session.readTransaction((tx) => tx.run(statement, values));
            session.close(); // 👈
            return (_b = exports.format.from((_a = result === null || result === void 0 ? void 0 : result.records[0]) === null || _a === void 0 ? void 0 : _a.get(0))) !== null && _b !== void 0 ? _b : null;
        },
        /**
         * Reads/writes values from/to the database.
         * Properties are available under `$data`
         */
        async write(statement, values) {
            var _a;
            const result = await session.writeTransaction((tx) => tx.run(statement, { data: exports.format.to(values) }));
            session.close(); // 👈
            return exports.format.from((_a = result === null || result === void 0 ? void 0 : result.records[0]) === null || _a === void 0 ? void 0 : _a.toObject());
        },
    };
}

The error I got after my try

Cannot begin a transaction on a closed session. 🤦‍♂️

[next-auth][error][adapter_error_getSessionAndUser] 
https://next-auth.js.org/errors#adapter_error_getsessionanduser Cannot begin a transaction on a closed session. {
  message: 'Cannot begin a transaction on a closed session.',
  stack: 'Neo4jError: Cannot begin a transaction on a closed session.\n' +
    '    at new Neo4jError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:77:16)\n' +
    '    at newError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:113:12)\n' +
    '    at Session._beginTransaction (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:277:40)\n' +
    '    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:394:77\n' +
    '    at TransactionExecutor.<anonymous> (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:116:46)\n' +
    '    at step (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:52:23)\n' +
    '    at Object.next (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:33:53)\n' +
    '    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:27:71\n' +
    '    at new Promise (<anonymous>)\n' +
    '    at __awaiter (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:23:12)',
  name: 'Neo4jError'
}
[next-auth][error][SESSION_ERROR] 
https://next-auth.js.org/errors#session_error Cannot begin a transaction on a closed session. Neo4jError: Cannot begin a transaction on a closed session.
    at new Neo4jError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:77:16)
    at newError (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/error.js:113:12)
    at Session._beginTransaction (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:277:40)
    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/session.js:394:77
    at TransactionExecutor.<anonymous> (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:116:46)
    at step (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:52:23)
    at Object.next (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:33:53)
    at /Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:27:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/vlad/www/issue-next-auth-neo4j/node_modules/neo4j-driver-core/lib/internal/transaction-executor.js:23:12) {
  name: 'GetSessionAndUserError',
  code: 'N/A'
}

So I came here for another perspective. 🎩 Of course, I’m willing to help, just let me know how 😇.

How to reproduce

  1. Clone the repository: https://github.com/vladutilie/issue-next-auth-neo4j
  2. Run $ yarn install
  3. Run $ cp .env.example .env
  4. Set up the fields in .env file (you will need a Neo4j database instance - I’m using Neo4j Desktop for local development but you can use an AuraDB or something similar as well)
  5. Run $ npm run dev in terminal
  6. Open the link http://localhost:3000/api/auth/signin in Chrome or Firefox, put your email and authenticate yourself
  7. You will see a text like this on the page: {"user":{"email":"you@email.com"},"expires":"2022-12-18T20:12:48.651Z"}
  8. Open a new tab in the browser and access a random page - this will trigger losing focus of the current page and will make SWR revalidate (re-fetch) the data from the API (/api/hello endpoint in this case)
  9. Click back on the initial tab after a few seconds
  10. You will see the text {"message":"Not authenticated."} in the page
  11. Go into your logs and you will see the error described above.

Expected behavior

After the 9th step from reproduction, on the page you should see the same text you saw first, something like {"user":{"email":"you@email.com"},"expires":"2022-12-18T20:12:48.651Z"} and the logs should not have that error, so the unstable_getServerSession function should return the authenticated user data.

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 2
  • Comments: 18

Most upvoted comments

@balazsorban44 @ndom91 Hi, I need some advice on my approaach to fix this, please.

It looks 2 (or more) adapter functions are running at the same time. Looking at the bug report I think getSessionAndUser is being run while still in the middle of a current adapter function.

I’ve checked (the neo4j adapter)[https://github.com/nextauthjs/next-auth/blob/main/packages/adapter-neo4j/src/index.ts] and all functions are async and have the correct await.

I think that unstable_getServerSession or some other part of core next-auth is not awaiting the return value of adapter functions. Hence allowing 2 to be run at the same time, causing the error.

I can see 2 approaches to a fix:

  • Ensure core always awaits the adapter function return value.
  • Change the neo4j adapter so that a new session is created for each transaction. To keep backward compatibility - this could be done by optionally passing the neo4j-driver as an adapter parameter.

Do you think my diagnosis is right? I can implement the neo4j-adapter change, plase advise.

Hey @vladutilie sorry i can’t dedicate a bunch of time to help but I can give some pointers.

  • This error is related to a neo4j session. I’ve seen it often before and it can most easilly be replicated by starting a neo4j session from the driver. Then closing with session.close() then attempting to read or write from the database with the closed session.
  • Edit: I’ve re-read the error message, it’s caused by running 2 or more simultanious database transactions with a single database session. Currently the adapter uses a single session for all database actions.
  • You could verify the session on this line with logging or a breakpoint. I think there is an isConnected() method or similar.
  • In the options set debug: true . You may get some more useful info.
  • Also, I don’t know how often a new driver / session is created in the neo4j example. I have the following code in a sepetate file so that the driver is created once per nodejs process
// neo4j.js
import neo4j from 'neo4j-driver';
let driver;
export default function getDriver() {
  if (!driver) {
    driver = neo4j.driver(url, neo4j.auth.basic(username, password), options);
  }
  return driver;
}

I have this adapter runing in production with pinned slightly old versions and jwt (not using unstable_getServerSession).

I suspect the error you’re seeing is because of some new feature or change (to this package or other dependancy) since the neo4j adapter was written.

Sorry I can’t assingn myself to fix this right now, my work load is huge. Regardless, let me know how you get on.

Running into the same problem

Hi @vladutilie could you try with neo4j-driver 4.x ?

It seems 5 was only rolled out a few months ago and the neo4j adapret is tested up to version 4.

This way at least we will know how to proceeed, additional tests/amends for v5 or futher investigation elsewhere.

FYI this error does not pop up on Vercel functions, so it verifies the hypothesis