TL;DR: Look up the parent record before you create the child. If the parent lookup misses, return a clear not-found message instead of letting the database throw P2003. The error means the id you are linking to does not exist.

```text
Foreign key constraint failed on the field: `authorId`
```

## Steps

1. Read the field name in the error. `authorId` tells you the relation that failed: the author you referenced does not exist.

2. Verify the parent exists before creating the child:
   ```ts
   const author = await prisma.user.findUnique({ where: { id: input.authorId } })
   if (!author) {
     return { ok: false, message: 'Author not found.' }
   }
   await prisma.post.create({ data URIs { title: input.title, authorId: input.authorId } })
   ```
   Expected: a bad author id returns your clean message; a good one creates the post.

3. Still catch P2003 as a backstop for races (the parent can be deleted between your check and your write):
   ```ts
   } catch (e) {
     if (e instanceof PrismaClientKnownRequestError && e.code === 'P2003') {
       return { ok: false, message: 'The linked record no longer exists.' }
     }
     throw e
   }
   ```
   Expected: concurrent deletes produce a friendly message, not a 500.

## When to use
- The exact code is P2003 on a create, update, or connect that references another record
- The referenced id comes from user input, a URL param, or another service

## When not to use
- P2002: that is a duplicate value, not a missing parent
- You are deleting a parent with children: that is a cascade / restrict policy question, set `onDelete` in the schema

## Compatibility
Prisma Client 4.x through 6.x. All relational providers.

### Variant phrasing: P2003 on `connect: { id }` in a nested write
Same fix: verify the id exists before the nested write, or catch P2003 around it.

### Variant phrasing: P2003 in a seed script
Seeds run in order; the parent seed must run (and commit) before the child seed. Check your seed order.

## Why it happens
The foreign key in the database guarantees the child always points at a real parent row. When the app sends an id that is not there (typo, stale client state, deleted parent), the database rejects the write and Prisma reports P2003. Checking first turns a database rejection into an application-level message.

## Edge cases
- `onDelete: Cascade` vs `Restrict`: deleting a parent with children fails differently; choose the policy your domain needs.
- UUID vs integer ids: a malformed id string can fail parsing before it ever reaches the constraint check.
- Multi-tenant apps: the parent may exist but belong to another tenant; scope the lookup by tenant, not just by id.