Nobody has started this yet — be first.
Business impact
Every account system eventually gets two signup requests for the same email arriving close enough together to race -- a double-submitted signup form, a flaky network causing a client to silently retry, two browser tabs. The sequential case ("register, then register again with the same email") already works correctly today and returns a clean 409. But the concurrent case -- both requests reaching the database before either has committed -- currently surfaces as a raw 500 Internal Server Error to whichever request loses the race. To a user that looks exactly like "the site crashed while I was signing up," not "you already have an account," which is a worse first impression and a guaranteed source of confused support requests precisely at the signup funnel's most sensitive moment.
Problem
POST /auth/register pre-checks for an existing email with prisma.user.findUnique and throws a clean Errors.Conflict(...) (a 409) if it finds one. But when two requests for the same brand-new email race each other, both can pass that pre-check before either call reaches prisma.user.create. Only one create can actually succeed; the database's own unique constraint on email rejects the other with a Prisma PrismaClientKnownRequestError (code: 'P2002', meta: { modelName: 'User', target: ['email'] }). The centralized error handler is supposed to catch that and turn it into a 409, via if (err && err.meta && err.meta.code === 'P2002') { ... } -- but Prisma puts the error code at the top level of the error object (err.code), not nested inside err.meta, so this check never matches and the request falls through to the generic 500 handler.
Current behavior
Under two genuinely concurrent POST /auth/register requests for the same brand-new email, one gets 201 and the other gets 500 -- not the clean 409 the sequential case already returns.
Expected behavior
Whichever of two concurrent same-email registrations loses the race gets a clean 409 CONFLICT, identical in shape to the response the sequential case already gets -- never a 500. Exactly one user row ends up in the database either way.
Steps to reproduce
cd nodejs/blog_api node -e " const request = require('supertest'); const app = require('./src/app')(); (async () => { const email = 'race-' + Date.now() + '@example.com'; const [a, b] = await Promise.all([ request(app).post('/auth/register').send({ email, password: 'password123' }), request(app).post('/auth/register').send({ email, password: 'password123' }), ]); console.log(a.status, b.status); // one 201, but the other is 500, not 409 })(); "
Why this matters
PrismaClientKnownRequestError.meta and .code are two different, unrelated properties on the same error object -- .code is the stable identifier ('P2002', 'P2025', ...) meant for exactly this kind of branching, while .meta is a free-form bag of per-error-type details (which fields collided, which record was missing, etc.). Reaching for err.meta.code instead of err.code is an easy mistake if you're picturing "the error's metadata" as one place error codes might live, but it means this whole branch is dead code until it's exercised by something that actually races -- which is exactly the class of bug that ships untested, because the obvious manual test (do it twice, one after another) doesn't trigger it.
Suggested approach
Compare the P2002 branch in src/middleware/errorHandler.js against the P2025 branch two blocks above it, which reads the code correctly. Also worth checking, in a Node REPL against a live duplicate insert, exactly what shape a real PrismaClientKnownRequestError has -- don't guess from memory.
Acceptance criteria
Verification
node_modules/.bin/jest --config practice-tickets/jest.config.js practice-tickets/tests/ticket06_duplicate_registration_race_500.test.js --verbose
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b fix/duplicate-registration-race-500Fix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/duplicate-registration-race-500Submit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign indocker compose down -vTo run locally without Docker, you need a local PostgreSQL instance:
npm install
cp .env.example .env # edit DATABASE_URL, JWT_SECRET, etc.
npm run prisma:migrate:dev # applies migrations to your local Postgres
npm run dev # starts with --watch on http://localhost:3000
Run the project's own test suite (real Postgres, not mocks -- point DATABASE_URL at a disposable database first):
npm test
Work the tickets in practice-tickets/ (ticket01 through ticket07); each has its own dedicated Jest test under practice-tickets/tests/, run via a separate Jest config (practice-tickets/jest.config.js) so plain npm test never picks them up:
docker compose up -d postgres # if it isn't already running
npm run prisma:migrate
./practice-tickets/run_tickets.sh # all 7, clean pass/fail summary
node_modules/.bin/jest --config practice-tickets/jest.config.js \
practice-tickets/tests/ticket01_pagination_skip_off_by_one.test.js --verbose # a single ticket
Level 1
Fix a bug
Read existing behaviour, correct it.