# KnowYourError.com — full text export > Every published error page, in markdown. Copy-paste fixes first, causes and FAQ after. Free and public; no scraping limits. ## PostgreSQL Error 42P01: Undefined table URL: https://knowyourerror.com/postgres/42p01-undefined-table/ Severity: ERROR · Retryable: no · HTTP 404 · Layer: Database Last verified: 2026-10-19 The query references a table or view that does not exist in any schema on the current search_path. Run your migrations, or schema-qualify the name and match its exact case. ### What you see ``` ERROR: relation "users" does not exist LINE 1: SELECT * FROM users; ^ ``` ### Likely causes - **Migrations not applied to this database** (High): The code was deployed before the migration that creates the table ran. Common when CI points at a fresh database or a different branch database. - **Table lives in another schema** (Medium): Postgres resolves unqualified names through search_path, which defaults to "$user", public. A table in schema app is invisible to SELECT * FROM users. - **Case-sensitive quoted identifier** (Medium): A table created as "Users" (quoted) keeps its capital letter. Unquoted users is folded to lowercase and does not match. ### Fixes #### Schema-qualify or fix the search_path (SQL) Qualified names do not depend on search_path. Setting search_path on the role applies to every new session for that user. ```sql SELECT id, email FROM app.users; -- or make it the default for this role ALTER ROLE api_user SET search_path = app, public; ``` #### Apply pending migrations before the app starts (CLI) migrate deploy applies only pending migrations and is safe to run on every boot. \dt *.users lists matching tables across all schemas. ```cli # Dockerfile CMD ["sh", "-c", "npx prisma migrate deploy && node dist/server.js"] # verify the table exists psql "$DATABASE_URL" -c '\dt *.users' ``` ### FAQ **Why does 42P01 happen only in production but not locally?** Your local database has migrations applied that production does not. Compare SELECT * FROM _prisma_migrations (or your tool's history table) on both. **Does 42P01 fire for views and sequences too?** Yes. Any relation — table, view, materialized view, sequence or index — that cannot be resolved raises undefined_table. **How do I find which schema a table is in?** Run SELECT table_schema FROM information_schema.tables WHERE table_name = 'users'. Then add that schema to search_path or qualify the name. ### Prevention - Run migrations as a release step in CI, not by hand. - Avoid quoted mixed-case identifiers; use snake_case. - Pin search_path per role instead of per connection string. ## PostgreSQL Error 23505: Unique violation URL: https://knowyourerror.com/postgres/23505-unique-violation/ Severity: ERROR · Retryable: no · HTTP 409 · Layer: Database Last verified: 2026-10-18 An INSERT or UPDATE produced a value that already exists in a column covered by a UNIQUE constraint or index. Use INSERT ... ON CONFLICT, or return 409 to the client. ### What you see ``` ERROR: duplicate key value violates unique constraint "users_email_key" DETAIL: Key (email)=(ada@example.com) already exists. ``` ### Likely causes - **Check-then-insert race condition** (High): Two requests both SELECT, see no row, then both INSERT. The second one hits the unique index. - **Sequence out of sync after import** (Medium): Rows were bulk-loaded with explicit ids, so the serial sequence still returns ids that already exist. Every new INSERT collides on the primary key. - **Retried request without idempotency** (Medium): A client or queue retried a successful INSERT after a timeout. The original write already committed. ### Fixes #### Use an atomic upsert instead of check-then-insert (SQL) ON CONFLICT resolves the race inside the database in a single statement. Use DO NOTHING if you only want to skip duplicates. ```sql INSERT INTO users (email, name) VALUES ($1, $2) ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name RETURNING id; ``` #### Reset a primary-key sequence after a bulk load (SQL) setval moves the sequence past the highest existing id. The false argument makes the next nextval() return exactly that value. ```sql SELECT setval( pg_get_serial_sequence('users', 'id'), COALESCE((SELECT MAX(id) FROM users), 0) + 1, false ); ``` ### FAQ **How do I get the constraint name from 23505 in node-postgres?** The pg error object has err.code === '23505' and err.constraint (e.g. users_email_key). err.detail contains the conflicting key. **Does ON CONFLICT DO NOTHING still consume a sequence value?** Yes. nextval() is evaluated before the conflict check, so gaps appear in serial ids. That is expected and harmless. **Is 23505 safe to retry?** Not as-is: the same values will collide again. Retry only after changing the input or switching to an upsert. ### Prevention - Enforce uniqueness in the database, never only in app code. - Map 23505 to HTTP 409 with the constraint name from err.constraint. - Send an Idempotency-Key on retried POSTs. ## PostgreSQL Error 53300: Too many connections URL: https://knowyourerror.com/postgres/53300-too-many-connections/ Severity: CRITICAL · Retryable: yes · HTTP 503 · Layer: Database Last verified: 2026-10-21 Every slot in max_connections is in use, so Postgres refuses new sessions. Put a pooler (PgBouncer, Supavisor, Neon pooled endpoint) in front and cap each app instance's pool size. ### What you see ``` psql: error: connection to server at "10.0.4.12", port 5432 failed: FATAL: sorry, too many clients already FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute ``` ### Likely causes - **Serverless functions each open a pool** (High): Every Lambda or Vercel function instance creates its own pool of N connections. 50 concurrent instances × 10 connections exceeds the default max_connections of 100. - **Leaked connections never released** (Medium): Code calls pool.connect() and forgets client.release() on an error path. The pool grows until the server is full. - **Dev hot-reload creates new clients** (Medium): Next.js or nodemon reloads re-run new PrismaClient() or new Pool() at module scope, leaving old pools connected. ### Fixes #### Always release clients and cap pool size (Node.js) finally guarantees release on every path. A small max per instance keeps instances × max under the server limit. ```node.js const pool = new Pool({ max: 5, idleTimeoutMillis: 10000 }); const client = await pool.connect(); try { const { rows } = await client.query('SELECT * FROM jobs'); return rows; } finally { client.release(); } ``` #### Route app traffic through a transaction pooler (CLI) A transaction pooler multiplexes thousands of client connections onto a few server connections. pg_stat_activity shows which app is hogging slots. ```cli # PgBouncer in transaction mode on 6432 DATABASE_URL="postgres://app:pw@db.internal:6432/app?pgbouncer=true" # inspect who holds connections psql -c "SELECT usename, application_name, state, count(*) FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;" ``` ### FAQ **Should I just raise max_connections?** Only modestly. Each backend uses several MB of RAM, and throughput degrades well before a few hundred active connections; a pooler scales further. **Why do I still have connections after my app stopped?** TCP keepalives may not have detected the dead client yet. Terminate them with SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename = 'app'. **What is the difference between 53300 and Prisma P2024?** 53300 means the Postgres server is full. P2024 means Prisma's own client-side pool had no free connection within pool_timeout. ### Prevention - Budget connections: instances × pool max + admin headroom < max_connections. - Set idle_in_transaction_session_timeout to kill stuck sessions. - Use a single PrismaClient/Pool per process (globalThis in dev). ## PostgreSQL Error 40P01: Deadlock detected URL: https://knowyourerror.com/postgres/40p01-deadlock-detected/ Severity: ERROR · Retryable: yes · HTTP 409 · Layer: Database Last verified: 2026-10-16 Two transactions each hold a row lock the other needs, so Postgres aborted one after deadlock_timeout (1s). Lock rows in a consistent order and retry the aborted transaction. ### What you see ``` ERROR: deadlock detected DETAIL: Process 48211 waits for ShareLock on transaction 9123412; blocked by process 48207. Process 48207 waits for ShareLock on transaction 9123410; blocked by process 48211. HINT: See server log for query details. CONTEXT: while updating tuple (0,7) in relation "accounts" ``` ### Likely causes - **Rows locked in inconsistent order** (High): Transaction A updates account 1 then 2 while B updates 2 then 1. Each waits on the other forever until the detector kills one. - **Foreign keys take implicit share locks** (Medium): Inserting a child row takes FOR KEY SHARE on the parent. Concurrent updates of the parent and inserts of children can cycle. - **Long transactions widen the window** (Low): Doing HTTP calls or heavy work inside a transaction keeps locks held for seconds, making collisions likely under load. ### Fixes #### Lock rows in a deterministic order (SQL) Every transfer now acquires locks in ascending id order, so no cycle can form. ```sql BEGIN; SELECT id FROM accounts WHERE id IN ($from, $to) ORDER BY id FOR UPDATE; UPDATE accounts SET balance = balance - 100 WHERE id = $from; UPDATE accounts SET balance = balance + 100 WHERE id = $to; COMMIT; ``` #### Retry the whole transaction on 40P01 (Node.js) The aborted transaction rolled back cleanly, so replaying it is safe. Jittered backoff avoids colliding again. ```node.js async function withRetry(fn, attempts = 3) { for (let i = 1; ; i++) { try { return await db.transaction(fn); } catch (err) { const retry = err.code === '40P01' || err.code === '40001'; if (!retry || i >= attempts) throw err; await new Promise(r => setTimeout(r, 50 * 2 ** i + Math.random() * 50)); } } } ``` ### FAQ **Which transaction does Postgres kill in a deadlock?** The one whose deadlock_timeout expired and ran the detector. It is not predictable, so every participant must be retry-safe. **How do I see the queries involved?** The full statements are in the server log next to the ERROR line. On managed Postgres, check the provider's log explorer. **Is 40P01 the same as a serialization failure?** No. 40001 serialization_failure comes from REPEATABLE READ/SERIALIZABLE conflicts; both are in class 40 and both should be retried. ### Prevention - Keep transactions short; no network calls inside them. - Set log_lock_waits = on to see waits before they deadlock. - Index foreign-key columns to shrink lock scope. ## PostgreSQL Error 08006: Connection failure URL: https://knowyourerror.com/postgres/08006-connection-failure/ Severity: CRITICAL · Retryable: yes · HTTP 503 · Layer: Network Last verified: 2026-10-12 The TCP connection to Postgres dropped mid-session (failover, restart, idle timeout at a proxy). Reconnect with backoff and set keepalives so pooled connections are validated. ### What you see ``` Error: Connection terminated unexpectedly at Connection. (/app/node_modules/pg/lib/client.js:132:73) SQLSTATE 08006: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. ``` ## PostgreSQL Error 42703: Undefined column URL: https://knowyourerror.com/postgres/42703-undefined-column/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: Database Last verified: 2026-10-14 The column name does not exist on that relation. Usually a camelCase column created with quotes; reference it as "createdAt" or rename it to snake_case. ### What you see ``` ERROR: column "createdat" does not exist LINE 1: SELECT id, createdAt FROM orders; ^ HINT: Perhaps you meant to reference the column "orders.createdAt". ``` ## PostgreSQL Error 57014: Query canceled URL: https://knowyourerror.com/postgres/57014-query-canceled/ Severity: ERROR · Retryable: yes · HTTP 504 · Layer: Database Last verified: 2026-10-09 The statement ran longer than statement_timeout, or was cancelled with pg_cancel_backend(). Add an index or raise the timeout for that one query with SET LOCAL statement_timeout. ### What you see ``` ERROR: canceling statement due to statement timeout ``` ## PostgreSQL Error 22P02: Invalid text representation URL: https://knowyourerror.com/postgres/22p02-invalid-text-representation/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: Database Last verified: 2026-10-07 A string could not be cast to the column type (uuid, integer, boolean, enum). Validate route params before querying; "undefined" usually means a missing JS variable. ### What you see ``` ERROR: invalid input syntax for type uuid: "undefined" LINE 1: SELECT * FROM users WHERE id = 'undefined'; ^ ``` ## PostgreSQL Error 23503: Foreign key violation URL: https://knowyourerror.com/postgres/23503-foreign-key-violation/ Severity: ERROR · Retryable: no · HTTP 409 · Layer: Database Last verified: 2026-10-11 The referenced parent row does not exist, or you deleted a parent that still has children. Insert parents first, or declare ON DELETE CASCADE / SET NULL. ### What you see ``` ERROR: insert or update on table "posts" violates foreign key constraint "posts_author_id_fkey" DETAIL: Key (author_id)=(42) is not present in table "users". ``` ## Prisma ORM Error P2002: Unique constraint failed URL: https://knowyourerror.com/prisma/p2002-unique-constraint-failed/ Severity: ERROR · Retryable: no · HTTP 409 · Layer: Database Last verified: 2026-10-20 Prisma tried to write a value that already exists in a @unique or @@unique field (Postgres 23505 underneath). Catch P2002 and return 409, or use upsert. ### What you see ``` PrismaClientKnownRequestError: Invalid `prisma.user.create()` invocation in /app/src/routes/signup.ts:18:34 15 const hash = await argon2.hash(body.password); 16 17 // create account → 18 const user = await prisma.user.create( Unique constraint failed on the fields: (`email`) code: 'P2002', meta: { modelName: 'User', target: [ 'email' ] } ``` ### Likely causes - **Duplicate signup or double submit** (High): The user clicked twice or the client retried after a timeout. The first create already committed the email. - **findFirst then create race** (Medium): Two requests both check for an existing row, find none, and both call create. Only the database constraint stops the second. - **Seed script re-run on populated DB** (Medium): prisma db seed with create() instead of upsert() fails on the second run. ### Fixes #### Catch P2002 and return a 409 (TypeScript) The unique index is the source of truth; mapping P2002 to 409 gives clients a clear, non-retryable answer. ```typescript import { Prisma } from '@prisma/client'; try { const user = await prisma.user.create({ data: { email, name } }); return Response.json(user, { status: 201 }); } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') { return Response.json({ error: 'Email already registered' }, { status: 409 }); } throw e; } ``` #### Use upsert for idempotent writes (TypeScript) upsert is safe to run repeatedly in seeds and webhooks. On Postgres, Prisma uses native INSERT ... ON CONFLICT when the where clause is a single unique field. ```typescript await prisma.user.upsert({ where: { email: 'admin@example.com' }, update: { role: 'ADMIN' }, create: { email: 'admin@example.com', role: 'ADMIN' }, }); ``` ### FAQ **Why is meta.target an index name instead of field names?** On some databases and older Prisma versions, target is the constraint name (e.g. User_email_key). Parse both forms or match on the substring. **Can upsert still throw P2002?** Yes, if Prisma falls back to a non-native upsert (select then insert) and two calls race. Retry once on P2002 in that case. **Does P2002 roll back my $transaction?** Yes. Any thrown error inside an interactive $transaction rolls back all prior writes in it. ### Prevention - Read e.meta.target to know which field collided. - Disable the submit button while the request is in flight. - Prefer upsert in seed scripts. ## Prisma ORM Error P2024: Connection pool timeout URL: https://knowyourerror.com/prisma/p2024-connection-pool-timeout/ Severity: CRITICAL · Retryable: yes · HTTP 503 · Layer: Database Last verified: 2026-10-22 All connections in Prisma's pool were busy for longer than pool_timeout (default 10s). Reuse one PrismaClient, size connection_limit for your platform, and use a pooler for serverless. ### What you see ``` PrismaClientKnownRequestError: Invalid `prisma.order.findMany()` invocation: Timed out fetching a new connection from the connection pool. More info: http://pris.ly/d/connection-pool (Current connection pool timeout: 10, connection limit: 5) code: 'P2024', meta: { modelName: 'Order', connection_limit: 5, timeout: 10 } ``` ### Likely causes - **Many PrismaClient instances** (High): Instantiating PrismaClient per request or on every hot reload creates a new pool each time. Connections pile up while each pool stays small. - **Pool too small for concurrency** (High): Default connection_limit is num_cpus × 2 + 1; in a 1-vCPU container that is 3. Promise.all over 50 queries queues behind 3 connections. - **Slow queries hold connections** (Medium): A missing index or long interactive $transaction keeps connections checked out, starving other requests. ### Fixes #### Share one PrismaClient across hot reloads (Next.js) Caching the client on globalThis survives Next.js dev reloads, so you keep one pool instead of one per reload. ```next.js // lib/db.ts import { PrismaClient } from '@prisma/client'; const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; export const prisma = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; ``` #### Tune connection_limit and pool_timeout in the URL (CLI) connection_limit caps the per-instance pool; pool_timeout is how long a query waits for a free connection. pgbouncer=true disables prepared statements for transaction-mode poolers. ```cli # serverless: small pool behind a pooler (PgBouncer / Neon -pooler host) DATABASE_URL="postgresql://app:pw@ep-cool-123-pooler.us-east-2.aws.neon.tech/app?pgbouncer=true&connection_limit=1&pool_timeout=20" # long-running server: bigger pool DATABASE_URL="postgresql://app:pw@db.example.com:5432/app?connection_limit=20&pool_timeout=20" ``` ### FAQ **What is the default Prisma connection_limit?** num_physical_cpus × 2 + 1. Set it explicitly in the connection string so behavior does not change across machines. **Should I set pool_timeout=0?** 0 disables the timeout, so requests wait forever instead of failing. Prefer a finite value and fix the underlying contention. **Is P2024 the same as Postgres "too many clients"?** No. P2024 is Prisma's client-side pool; Postgres 53300 means the server itself rejected the connection. Too many Prisma pools can cause both. ### Prevention - Never call new PrismaClient() inside a request handler. - Batch with Promise.all in chunks no larger than connection_limit. - Enable Prisma metrics to watch prisma_pool_connections_busy. ## Prisma ORM Error P1001: Can't reach database server URL: https://knowyourerror.com/prisma/p1001-cant-reach-database-server/ Severity: CRITICAL · Retryable: yes · HTTP 503 · Layer: Network Last verified: 2026-10-17 Prisma could not open a TCP connection to the host and port in DATABASE_URL. Check the host, port, firewall/IP allowlist and whether the database is paused or still booting. ### What you see ``` Error: P1001: Can't reach database server at `db.example.com:5432` Please make sure your database server is running at `db.example.com:5432`. ``` ### Likely causes - **Wrong host inside Docker** (High): DATABASE_URL uses localhost, but inside a container localhost is the container itself. Use the compose service name (e.g. db). - **IP allowlist or security group** (Medium): Managed databases (RDS, Supabase, Neon IP Allow) drop traffic from unlisted IPs, so the connect times out. - **Database suspended or cold-starting** (Medium): Serverless Postgres scales to zero; the first connection after idle can exceed connect_timeout (default 5s). ### Fixes #### Use the compose service name, not localhost (CLI) Compose puts services on a shared network where each service name resolves via DNS. service_healthy waits for the db healthcheck before starting the app. ```cli # docker-compose.yml (app service) environment: DATABASE_URL: postgresql://postgres:postgres@db:5432/app depends_on: db: condition: service_healthy ``` #### Raise connect_timeout for cold starts and verify reachability (CLI) connect_timeout gives a suspended compute time to wake. nc tells you whether the problem is network-level before you debug Prisma. ```cli DATABASE_URL="postgresql://app:pw@ep-cool-123.us-east-2.aws.neon.tech/app?sslmode=require&connect_timeout=15" # check TCP reachability from the same machine nc -vz ep-cool-123.us-east-2.aws.neon.tech 5432 ``` ### FAQ **P1001 works locally but fails in GitHub Actions — why?** The runner cannot reach a private database, or the DB allowlist excludes GitHub IPs. Use a service container for tests or a self-hosted runner in the VPC. **Does P1001 mean my password is wrong?** No. P1001 happens before authentication; wrong credentials produce P1000. **Do I need sslmode=require?** Most managed providers reject non-TLS connections. A rejected TLS negotiation can surface as P1001 or P1011 depending on the provider. ### Prevention - Add a healthcheck and depends_on for the db container. - Allowlist CI runner and server egress IPs. - Log the DATABASE_URL host (never the password) on boot. ## Prisma ORM Error P2025: Record not found URL: https://knowyourerror.com/prisma/p2025-record-not-found/ Severity: ERROR · Retryable: no · HTTP 404 · Layer: Database Last verified: 2026-10-15 update, delete, findUniqueOrThrow or a nested connect targeted a row that does not exist. Map P2025 to 404, or use updateMany/deleteMany when a missing row is acceptable. ### What you see ``` PrismaClientKnownRequestError: Invalid `prisma.post.update()` invocation in /app/src/routes/posts.ts:41:28 An operation failed because it depends on one or more records that were required but not found. No record was found for an update. code: 'P2025', meta: { modelName: 'Post', cause: 'Record to update not found.' } ``` ### Likely causes - **Stale or wrong id from client** (High): The row was deleted by another request, or the id belongs to another tenant filtered out by where. - **Extra filters in a unique where** (Medium): where: { id, authorId } on update requires both to match. A non-owner request finds no row and throws P2025. - **Nested connect to missing relation** (Low): connect: { id } on a relation whose target row does not exist also raises P2025. ### Fixes #### Return 404 instead of a 500 (TypeScript) A missing row is a client-facing condition, not a server crash. ```typescript import { Prisma } from '@prisma/client'; try { const post = await prisma.post.delete({ where: { id } }); return Response.json(post); } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2025') { return Response.json({ error: 'Not found' }, { status: 404 }); } throw e; } ``` #### Use deleteMany when absence is fine (TypeScript) updateMany/deleteMany never throw P2025; they return a count you can check. ```typescript const { count } = await prisma.session.deleteMany({ where: { token } }); // count === 0 means it was already gone ``` ### FAQ **Why did rejectOnNotFound stop working?** It was removed in Prisma 5. Use findUniqueOrThrow / findFirstOrThrow, which throw P2025. **Is P2025 thrown inside $transaction?** Yes, and it rolls back the whole transaction. Check existence first if partial success is acceptable. **How do I tell which relation was missing?** Read e.meta.cause and e.meta.modelName; nested writes include the relation name in the message. ### Prevention - Use findUnique + null check for reads, not findUniqueOrThrow, when 404 is expected. - Scope where clauses by tenant and treat P2025 as 404. - Make delete endpoints idempotent with deleteMany. ## Prisma ORM Error P1000: Authentication failed URL: https://knowyourerror.com/prisma/p1000-authentication-failed/ Severity: CRITICAL · Retryable: no · HTTP 401 · Layer: Database Last verified: 2026-10-10 The server rejected the user/password in DATABASE_URL. URL-encode special characters in the password (@ → %40, # → %23) and confirm the role exists. ### What you see ``` Error: P1000: Authentication failed against database server at `localhost`, the provided database credentials for `postgres` are not valid. Please make sure to provide valid database credentials for the database server at `localhost`. ``` ## Prisma ORM Error P2003: Foreign key constraint failed URL: https://knowyourerror.com/prisma/p2003-foreign-key-constraint-failed/ Severity: ERROR · Retryable: no · HTTP 409 · Layer: Database Last verified: 2026-10-13 The referenced parent row does not exist, or a delete would orphan children. Create the parent first or set onDelete: Cascade on the relation. ### What you see ``` PrismaClientKnownRequestError: Invalid `prisma.post.create()` invocation: Foreign key constraint violated on the constraint: `Post_authorId_fkey` code: 'P2003', meta: { modelName: 'Post', constraint: 'Post_authorId_fkey' } ``` ## Prisma ORM Error P2021: Table does not exist URL: https://knowyourerror.com/prisma/p2021-table-does-not-exist/ Severity: ERROR · Retryable: no · HTTP 500 · Layer: Database Last verified: 2026-10-08 The schema has a model whose table was never created in this database. Run prisma migrate deploy (or db push in prototypes) against the same DATABASE_URL. ### What you see ``` PrismaClientKnownRequestError: Invalid `prisma.user.findMany()` invocation: The table `public.User` does not exist in the current database. code: 'P2021', meta: { modelName: 'User', table: 'public.User' } ``` ## Prisma ORM Error P3009: Failed migrations found URL: https://knowyourerror.com/prisma/p3009-failed-migrations/ Severity: CRITICAL · Retryable: no · Layer: Database Last verified: 2026-10-06 A previous migrate deploy failed halfway and is recorded as failed in _prisma_migrations. Fix the DB state, then run prisma migrate resolve --rolled-back (or --applied) with the migration name. ### What you see ``` Error: P3009 migrate found failed migrations in the target database, new migrations will not be applied. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve The `20261012081402_add_orders` migration started at 2026-10-12 08:14:02.123 UTC failed ``` ## Prisma ORM Error P6004: Accelerate query timeout URL: https://knowyourerror.com/prisma/p6004-accelerate-query-timeout/ Severity: ERROR · Retryable: yes · HTTP 504 · Layer: API Gateway Last verified: 2026-10-05 A query through Prisma Accelerate exceeded the global query timeout. Add indexes, paginate large reads, or raise the limit in the Accelerate project settings. ### What you see ``` PrismaClientKnownRequestError: The global timeout of Accelerate has been exceeded. code: 'P6004', meta: { modelName: 'Report' } ``` ## Stripe Error insufficient_funds: Insufficient funds URL: https://knowyourerror.com/stripe/insufficient-funds/ Severity: WARNING · Retryable: no · HTTP 402 · Layer: API Gateway Last verified: 2026-10-21 The issuer declined because the account lacks available balance or credit. Show the customer a clear message and ask for another payment method; do not auto-retry the same card immediately. ### What you see ``` { "error": { "charge": "ch_3QJ8xY2eZvKYlo2C0aBcDeFg", "code": "card_declined", "decline_code": "insufficient_funds", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card has insufficient funds.", "payment_intent": { "id": "pi_3QJ8xY2eZvKYlo2C0hIjKlMn", "status": "requires_payment_method" }, "type": "card_error" } } ``` ### Likely causes - **Customer balance or credit limit** (High): The debit balance or remaining credit line is lower than the amount. The issuer returns this specific decline_code. - **Pending authorizations hold funds** (Medium): Earlier uncaptured authorizations (yours or other merchants') reduce available balance until they expire or are captured. - **Off-session renewal on a drained card** (Medium): Subscription renewals charge without the customer present, so the decline is only seen in invoice.payment_failed. ### Fixes #### Branch on decline_code and keep the PaymentIntent (Node.js) Card errors are customer-actionable, not server faults. The PaymentIntent returns to requires_payment_method and can be confirmed again with a new card. ```node.js try { await stripe.paymentIntents.confirm(piId); } catch (err) { if (err.type === 'StripeCardError') { const msg = err.decline_code === 'insufficient_funds' ? 'Your card has insufficient funds. Try another card.' : err.message; return res.status(402).json({ error: msg, paymentIntent: piId }); } throw err; } ``` #### Recover failed renewals from the invoice.payment_failed webhook (TypeScript) Insufficient funds often clears after payday, and Smart Retries schedules the next attempt. Downgrade only on customer.subscription.updated with status unpaid or canceled. ```typescript case 'invoice.payment_failed': { const invoice = event.data.object as Stripe.Invoice; await sendEmail(invoice.customer_email!, 'payment-failed', { payUrl: invoice.hosted_invoice_url, // customer can pay with another card attempt: invoice.attempt_count, nextRetry: invoice.next_payment_attempt, // unix ts, null when retries are exhausted }); break; } ``` ### FAQ **How do I test insufficient_funds in test mode?** Use card 4000 0000 0000 9995 or the PaymentMethod pm_card_visa_chargeDeclinedInsufficientFunds. Both decline with decline_code insufficient_funds. **Is the code field insufficient_funds or card_declined?** code is card_declined and decline_code is insufficient_funds. Always read decline_code for the reason. **Does a decline cost a fee?** Stripe does not charge a processing fee for declined card payments in most regions. Excessive retries can still trigger issuer risk flags. ### Prevention - Show err.message for card_error; it is safe for customers. - Listen to invoice.payment_failed to email customers. - Never retry the same card in a tight loop; issuers flag it. ## Stripe Error generic_decline: Generic decline (card_declined) URL: https://knowyourerror.com/stripe/generic-decline/ Severity: WARNING · Retryable: no · HTTP 402 · Layer: API Gateway Last verified: 2026-10-19 The issuer declined without giving Stripe a reason. Ask the customer to contact their bank or use another card; check Radar if many cards decline at once. ### What you see ``` { "error": { "code": "card_declined", "decline_code": "generic_decline", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "request_log_url": "https://dashboard.stripe.com/test/logs/req_9aBcD1eFgH2iJk", "type": "card_error" } } ``` ### Likely causes - **Issuer fraud rules, no reason given** (High): Banks often decline cross-border, unusual-amount or new-merchant payments and return a generic reason to avoid tipping off fraudsters. - **Card not enabled for online payments** (Medium): Some debit and prepaid cards are blocked for card-not-present or international transactions. - **Radar or merchant block rule** (Low): A custom Radar rule can block the payment; then the decline shows outcome.type blocked instead of issuer_declined. ### Fixes #### Inspect the charge outcome before guessing (Node.js) outcome.type tells you whether the issuer or Radar declined, which decides whether the customer or you must act. ```node.js const pi = await stripe.paymentIntents.retrieve(piId, { expand: ['latest_charge'], }); const o = pi.latest_charge?.outcome; console.log(o?.type, o?.network_status, o?.reason, o?.risk_level); // issuer_declined | blocked, declined_by_network, ..., normal|elevated|highest ``` #### Collect a new payment method client-side (TypeScript) Clearing the Payment Element prompts a new card while reusing the same PaymentIntent, so you do not create duplicate intents. ```typescript const { error } = await stripe.confirmPayment({ elements, confirmParams: { return_url: window.location.href }, redirect: 'if_required', }); if (error?.type === 'card_error') { setMessage(error.message ?? 'Your card was declined.'); elements.getElement('payment')?.clear(); } ``` ### FAQ **How do I test generic_decline?** Use card 4000 0000 0000 0002 in test mode. It returns card_declined with decline_code generic_decline. **Can Stripe tell me why the bank declined?** No. generic_decline means the issuer gave no reason; only the cardholder can get details from their bank. **Should I retry a generic decline?** Not automatically with the same card. Visa and Mastercard penalize repeated retries of declined cards. ### Prevention - Enable 3D Secure via automatic payment methods to lower declines. - Pass billing address and email to improve issuer approval. - Watch decline rate per BIN country in Radar. ## Stripe Error StripeSignatureVerificationError: Webhook signature verification failed URL: https://knowyourerror.com/stripe/webhook-signature-verification-failed/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: API Gateway Last verified: 2026-10-23 The HMAC of the body you passed does not match the Stripe-Signature header. Pass the raw, unparsed request body and the whsec_ secret of this exact endpoint. ### What you see ``` StripeSignatureVerificationError: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? If a webhook request is being forwarded by a third-party tool, ensure that the exact request body, including JSON formatting and new line style, is preserved. Learn more about webhook signing and explore webhook integration examples for various frameworks at https://docs.stripe.com/webhooks/signature at validateComputedSignature (/app/node_modules/stripe/cjs/Webhooks.js:149:19) at Object.verifyHeaderAsync (/app/node_modules/stripe/cjs/Webhooks.js:98:20) ``` ### Likely causes - **Body parsed as JSON before verify** (High): express.json() or a framework re-serializes the body, changing whitespace and key order. The signature was computed over the original bytes. - **Wrong signing secret** (High): Each endpoint, and each stripe listen session, has its own whsec_ secret. Test vs live and CLI vs Dashboard secrets differ. - **Timestamp outside tolerance** (Low): The t= timestamp is older than the 300-second default tolerance, e.g. replayed events or a skewed server clock. ### Fixes #### Use express.raw on the webhook route only (Node.js) express.raw keeps req.body as a Buffer of the exact bytes Stripe signed. Register it before any global JSON parser. ```node.js app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => { let event; try { event = stripe.webhooks.constructEvent( req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET); } catch (err) { return res.status(400).send(`Webhook Error: ${err.message}`); } res.json({ received: true }); }); app.use(express.json()); ``` #### Read req.text() in an App Router route handler (Next.js) JSON.stringify(await req.json()) never reproduces the original bytes. req.text() returns the body exactly as sent. ```next.js export async function POST(req: Request) { const body = await req.text(); const sig = req.headers.get('stripe-signature')!; try { const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!); // handle event.type return Response.json({ received: true }); } catch (err) { return new Response(`Webhook Error: ${(err as Error).message}`, { status: 400 }); } } ``` ### FAQ **Why does verification pass with stripe listen but fail in production?** stripe listen prints its own whsec_ secret. Production must use the secret from the Dashboard endpoint (Developers > Webhooks > Signing secret). **Does a proxy or API gateway break the signature?** Yes, if it re-encodes, decompresses or pretty-prints the JSON body. Forward the body byte-for-byte. **Can I skip verification in development?** You can, but you then accept forged events. Use stripe listen --forward-to and its secret instead. ### Prevention - Store whsec_ per environment; log only its last 4 chars. - Return 2xx fast and process events in a queue. - Make handlers idempotent on event.id; Stripe retries for up to 3 days. ## Cloudflare Error 1015: You are being rate limited URL: https://knowyourerror.com/cloudflare/error-1015-you-are-being-rate-limited/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: CDN / Edge Last verified: 2026-10-22 A Cloudflare rate limiting rule on the site matched your IP or key and is blocking you for the mitigation period. As a client, back off per Retry-After; as the owner, loosen or scope the rule. ### What you see ``` HTTP/2 429 server: cloudflare cf-ray: 8d2f4a1b7c3e2f10-FRA retry-after: 60 content-type: text/html; charset=UTF-8 Error 1015 Ray ID: 8d2f4a1b7c3e2f10 • 2026-10-22 09:14:03 UTC You are being rate limited The owner of this website (api.example.com) has banned you temporarily from accessing this website. ``` ### Likely causes - **Client exceeded a rate limiting rule** (High): Scrapers, retry loops or CI jobs from one IP send more requests than the rule allows per period. Cloudflare blocks until the mitigation timeout ends. - **Shared egress IP counted as one client** (Medium): Corporate NAT, CGNAT or a serverless platform's shared IPs aggregate many real users into a single counter. - **Rule too broad for API traffic** (Medium): A rule written for /login also matches /api/*, so legitimate high-frequency API clients get blocked. ### Fixes #### Back off on 429 and honor Retry-After (TypeScript) Retrying immediately extends the ban. Waiting Retry-After seconds lets the counter reset. ```typescript async function get(url: string, tries = 5): Promise { for (let i = 0; ; i++) { const res = await fetch(url); if (res.status !== 429 || i >= tries) return res; const wait = Number(res.headers.get('retry-after') ?? 2 ** i) * 1000; await new Promise(r => setTimeout(r, wait + Math.random() * 250)); } } ``` #### Scope the rule by path and count per API key (JSON) Counting per API key instead of per IP stops NAT users from sharing a quota; a shorter mitigation_timeout limits collateral damage. Counting by header requires an Enterprise plan; use ip.src otherwise. ```json { "expression": "starts_with(http.request.uri.path, \"/api/\")", "action": "block", "ratelimit": { "characteristics": ["http.request.headers[\"x-api-key\"][0]", "cf.colo.id"], "period": 60, "requests_per_period": 600, "mitigation_timeout": 60 } } ``` ### FAQ **How long does a Cloudflare 1015 ban last?** Until the rule's mitigation timeout (from 10 seconds up to a day, depending on plan) expires. Repeated violations restart it. **Is 1015 caused by Cloudflare or by the site?** By the site owner's rate limiting rule. Cloudflare only enforces it, so only the owner can change the threshold. **Will a VPN fix error 1015?** A new IP gets a fresh counter, but if the rule counts by header or cookie you will be blocked again. Slow down instead. ### Prevention - Expose rate limits in your API docs with Retry-After. - Use Security Events in the dashboard to see which rule fired. - Exempt health checks and known partners with a skip rule. ## Stripe Error rate_limit: Rate limit exceeded URL: https://knowyourerror.com/stripe/rate-limit/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: API Gateway Last verified: 2026-10-14 You exceeded Stripe's per-account request rate (by default 100 ops/s in live mode, 25 in test mode). Retry with exponential backoff and stop polling — use webhooks and list pagination instead. ### What you see ``` { "error": { "code": "rate_limit", "doc_url": "https://stripe.com/docs/error-codes/rate-limit", "message": "Request rate limit exceeded. Learn more about rate limits here https://stripe.com/docs/rate-limits.", "type": "invalid_request_error" } } ``` ### Likely causes - **Bulk jobs without concurrency limits** (High): Backfills or migrations fire thousands of requests via Promise.all, spiking above the per-second limit. - **Polling instead of webhooks** (Medium): Retrieving PaymentIntents or Subscriptions every few seconds to detect status changes burns the read budget. - **Test mode has lower limits** (Medium): Load tests in test mode hit the 25 ops/s limit much sooner than production would. ### Fixes #### Enable SDK network retries (Node.js) stripe-node retries 429s with backoff and reuses the same idempotency key, so POST retries cannot double-charge. ```node.js const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { maxNetworkRetries: 3, // exponential backoff on 429, 409 lock_timeout and network errors timeout: 20000, }); ``` #### Cap concurrency in bulk jobs (TypeScript) Ten in-flight requests at ~100ms each stays near 100 req/s even before retries kick in. ```typescript import pLimit from 'p-limit'; const limit = pLimit(10); await Promise.all(customers.map(c => limit(() => stripe.customers.update(c.id, { metadata: { plan: c.plan } })))); ``` ### FAQ **What is Stripe's API rate limit?** By default 100 read and 100 write operations per second in live mode and 25 each in test mode, with lower limits on some endpoints such as Search. **Does Stripe send a Retry-After header?** Not reliably. Use exponential backoff with jitter, which the official SDKs implement via maxNetworkRetries. **Is rate_limit the same as lock_timeout?** No. lock_timeout (also 429) means concurrent requests touched the same object; serialize writes per object. ### Prevention - Prefer webhooks over polling for state changes. - Use auto-pagination (for await) instead of parallel page fetches. - Run load tests against stripe-mock, not test mode. ## Stripe Error expired_card: Expired card URL: https://knowyourerror.com/stripe/expired-card/ Severity: WARNING · Retryable: no · HTTP 402 · Layer: API Gateway Last verified: 2026-10-10 The card's expiry date has passed. Ask for a new card; for subscriptions, Stripe's card account updater refreshes many cards automatically. ### What you see ``` { "error": { "code": "expired_card", "decline_code": "expired_card", "doc_url": "https://stripe.com/docs/error-codes/expired-card", "message": "Your card has expired.", "param": "exp_month", "type": "card_error" } } ``` ## Stripe Error incorrect_cvc: Incorrect CVC URL: https://knowyourerror.com/stripe/incorrect-cvc/ Severity: WARNING · Retryable: no · HTTP 402 · Layer: API Gateway Last verified: 2026-10-09 The issuer rejected the security code. Let the customer re-enter it; test with card 4000 0000 0000 0127. ### What you see ``` { "error": { "code": "incorrect_cvc", "decline_code": "incorrect_cvc", "doc_url": "https://stripe.com/docs/error-codes/incorrect-cvc", "message": "Your card's security code is incorrect.", "param": "cvc", "type": "card_error" } } ``` ## Stripe Error authentication_required: Authentication required (3DS) URL: https://knowyourerror.com/stripe/authentication-required/ Severity: WARNING · Retryable: no · HTTP 402 · Layer: API Gateway Last verified: 2026-10-16 The issuer requires 3D Secure (SCA) but the payment was confirmed off-session. Bring the customer back on-session and confirm with stripe.confirmCardPayment / confirmPayment. ### What you see ``` { "error": { "code": "authentication_required", "decline_code": "authentication_required", "doc_url": "https://stripe.com/docs/error-codes/authentication-required", "message": "Your card was declined. This transaction requires authentication.", "payment_intent": { "id": "pi_3QK1aB2eZvKYlo2C1xYz", "status": "requires_payment_method" }, "type": "card_error" } } ``` ## Stripe Error resource_missing: No such resource URL: https://knowyourerror.com/stripe/resource-missing/ Severity: ERROR · Retryable: no · HTTP 404 · Layer: API Gateway Last verified: 2026-10-12 The ID does not exist in this account and mode. Usually a test-mode ID used with a live key, or an object on a Connect account requested without the Stripe-Account header. ### What you see ``` { "error": { "code": "resource_missing", "doc_url": "https://stripe.com/docs/error-codes/resource-missing", "message": "No such customer: 'cus_QZ8aBcDeFgHiJk'", "param": "id", "type": "invalid_request_error" } } ``` ## Cloudflare Error 524: A timeout occurred URL: https://knowyourerror.com/cloudflare/error-524-a-timeout-occurred/ Severity: ERROR · Retryable: yes · HTTP 524 · Layer: CDN / Edge Last verified: 2026-10-17 The origin accepted the connection but sent no HTTP response within 100 seconds. Move long jobs to a background queue and return 202, or use a grey-clouded subdomain for long requests. ### What you see ``` Error 524 Ray ID: 8d2fb07e9fac5b46 • 2026-10-17 10:09:15 UTC A timeout occurred ``` ## Cloudflare Error 522: Connection timed out URL: https://knowyourerror.com/cloudflare/error-522-connection-timed-out/ Severity: CRITICAL · Retryable: yes · HTTP 522 · Layer: Network Last verified: 2026-10-20 Cloudflare could not complete a TCP handshake with your origin within ~19 seconds. Allow Cloudflare IP ranges in your firewall, check the origin IP in DNS, and confirm the server listens on 0.0.0.0:443/80. ### What you see ``` HTTP/2 522 server: cloudflare cf-ray: 8d2f51c09a7b3e22-IAD content-type: text/html; charset=UTF-8 Error 522 Ray ID: 8d2f51c09a7b3e22 • 2026-10-20 14:02:51 UTC Connection timed out You Browser Working Ashburn Cloudflare Working www.example.com Host Error ``` ### Likely causes - **Firewall drops Cloudflare IPs** (High): iptables, ufw, a security group or fail2ban drops SYN packets from Cloudflare ranges. No RST is sent, so the connect times out. - **DNS points to the wrong origin IP** (Medium): The proxied A record still points at an old server or a private IP that Cloudflare cannot route to. - **Origin overloaded or bound to localhost** (Medium): The backlog is full, or the app listens on 127.0.0.1 only, so external connections never complete. ### Fixes #### Allow Cloudflare IP ranges on the origin (CLI) Cloudflare connects from its published ranges only. Allowing them (and nothing else) keeps the origin private and reachable. ```cli for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do sudo ufw allow proto tcp from "$ip" to any port 443 sudo ufw allow proto tcp from "$ip" to any port 80 done sudo ufw reload ``` #### Listen on all interfaces, not localhost (Node.js) A socket bound to 127.0.0.1 only accepts local connections, so Cloudflare's SYN is never answered. ```node.js server.listen(443, '0.0.0.0', () => { console.log('listening on 0.0.0.0:443'); }); // verify from another host: nc -vz 443 ``` ### FAQ **What is the difference between Cloudflare 522 and 524?** 522 means the TCP connection never opened. 524 means it opened but the origin did not send an HTTP response within 100 seconds. **Why do I get 522 only from some countries?** Geo-blocking or rate limiting on the origin firewall may drop specific Cloudflare data center ranges. **Can I raise the 522 timeout?** No. The connection timeout is fixed; fix reachability or capacity at the origin. ### Prevention - Monitor the origin directly, bypassing Cloudflare. - Use Cloudflare Tunnel to avoid exposing origin ports. - Alert on 522 rate in Cloudflare Analytics. ## Cloudflare Error 525: SSL handshake failed URL: https://knowyourerror.com/cloudflare/error-525-ssl-handshake-failed/ Severity: CRITICAL · Retryable: no · HTTP 525 · Layer: CDN / Edge Last verified: 2026-10-18 Cloudflare reached your origin on 443 but the TLS handshake failed (SSL mode Full or Full (strict)). Serve a certificate for the hostname on port 443 with SNI and TLS 1.2+, or install a Cloudflare Origin CA cert. ### What you see ``` HTTP/2 525 server: cloudflare cf-ray: 8d2f6b2e1d4c5a90-AMS Error 525 Ray ID: 8d2f6b2e1d4c5a90 • 2026-10-18 07:41:12 UTC SSL handshake failed ``` ### Likely causes - **No certificate configured on 443** (High): The origin listens on 443 without TLS, or the vhost for this hostname has no ssl_certificate, so the handshake aborts. - **SNI not supported or wrong vhost** (Medium): Cloudflare always sends SNI. If the origin has no matching server block it may close the connection. - **No overlapping cipher or protocol** (Low): The origin only allows TLS 1.0/1.1 or exotic ciphers that Cloudflare no longer offers. ### Fixes #### Test the origin handshake directly, then issue a cert (CLI) s_client shows the exact alert (handshake failure, no peer certificate). Once a valid cert is served, Full (strict) mode works. ```cli # talk to the origin IP with the real SNI openssl s_client -connect 203.0.113.10:443 -servername www.example.com -tls1_2 Events; visitors: contact the site with the Ray ID. ### What you see ``` Error 1020 Ray ID: 8d2f7c3a5b6e1d02 • 2026-10-15 11:22:09 UTC Access denied This website is using a security service to protect itself from online attacks. ``` ## Cloudflare Error 1016: Origin DNS error URL: https://knowyourerror.com/cloudflare/error-1016-origin-dns-error/ Severity: CRITICAL · Retryable: no · HTTP 530 · Layer: CDN / Edge Last verified: 2026-10-11 Cloudflare cannot resolve the origin hostname, usually a proxied CNAME pointing at a deleted or misspelled target. Fix the DNS record or point it at a resolvable host. ### What you see ``` HTTP/2 530 server: cloudflare Error 1016 Ray ID: 8d2f8d4b6c7f2e13 • 2026-10-11 16:05:44 UTC Origin DNS error ``` ## Cloudflare Error 520: Web server returned an unknown error URL: https://knowyourerror.com/cloudflare/error-520-web-server-returned-unknown-error/ Severity: ERROR · Retryable: yes · HTTP 520 · Layer: CDN / Edge Last verified: 2026-10-13 The origin returned an empty, invalid or reset response (crash, oversized headers > 128 KB, connection reset). Check origin error logs at the Ray ID timestamp. ### What you see ``` Error 520 Ray ID: 8d2f9e5c7d8a3f24 • 2026-10-13 03:18:27 UTC Web server is returning an unknown error ``` ## Cloudflare Error 521: Web server is down URL: https://knowyourerror.com/cloudflare/error-521-web-server-is-down/ Severity: CRITICAL · Retryable: yes · HTTP 521 · Layer: Network Last verified: 2026-10-14 The origin actively refused the connection (TCP RST): the web server is stopped or a firewall rejects Cloudflare IPs. Start the service and allow Cloudflare ranges. ### What you see ``` Error 521 Ray ID: 8d2faf6d8e9b4a35 • 2026-10-14 22:51:30 UTC Web server is down ``` ## Cloudflare Error 526: Invalid SSL certificate URL: https://knowyourerror.com/cloudflare/error-526-invalid-ssl-certificate/ Severity: CRITICAL · Retryable: no · HTTP 526 · Layer: CDN / Edge Last verified: 2026-10-12 In Full (strict) mode the origin certificate is expired, self-signed or does not cover the hostname. Renew it or install a Cloudflare Origin CA certificate. ### What you see ``` Error 526 Ray ID: 8d2fc18fa0bd6c57 • 2026-10-12 05:33:48 UTC Invalid SSL certificate ``` ## HTTP Status Codes Error 401: Unauthorized URL: https://knowyourerror.com/http/401-unauthorized/ Severity: ERROR · Retryable: no · HTTP 401 · Layer: API Gateway Last verified: 2026-10-21 The request has no valid credentials: the token is missing, malformed or expired. Send Authorization: Bearer , and refresh the token on 401 before retrying once. ### What you see ``` HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="The access token expired" Content-Type: application/json {"error":"invalid_token","error_description":"The access token expired"} ``` ### Likely causes - **Expired access token** (High): Short-lived JWTs (often 5–60 min) expire while the client keeps using them. The server answers 401 with error="invalid_token". - **Authorization header stripped** (Medium): A redirect to another host, a proxy or a CORS preflight drops the Authorization header, so the server sees an anonymous request. - **Wrong scheme or format** (Medium): Sending the raw token without "Bearer ", using Basic for a Bearer API, or a stray newline in an env var. ### Fixes #### Refresh once on 401, then retry (TypeScript) A single refresh-and-retry handles expiry without loops. Prefix the token with the Bearer scheme. ```typescript async function api(path: string, init: RequestInit = {}, retried = false): Promise { const res = await fetch(path, { ...init, headers: { ...init.headers, Authorization: `Bearer ${getAccessToken()}` }, }); if (res.status === 401 && !retried) { await refreshAccessToken(); return api(path, init, true); } return res; } ``` #### Check what the server actually receives (CLI) -i shows the WWW-Authenticate header with the exact reason. Stripping CR/LF removes invisible characters from copied tokens. ```cli curl -i https://api.example.com/v1/me \ -H "Authorization: Bearer $(printf %s "$TOKEN" | tr -d '\r\n')" # inspect JWT expiry echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .exp ``` ### FAQ **What is the difference between 401 and 403?** 401 means "who are you?" — authentication failed or is missing. 403 means "I know who you are, and you are not allowed". **Should a wrong password return 401?** For an API call with bad credentials, yes. A login form endpoint may also return 400 or 422 with a generic message. **Why do I get 401 only in production?** Often a reverse proxy strips Authorization or the env var holding the secret differs. Log whether the header is present (not its value). ### Prevention - Always return WWW-Authenticate with a 401. - Refresh tokens proactively a minute before exp. - Do not follow cross-origin redirects with credentials. ## HTTP Status Codes Error 403: Forbidden URL: https://knowyourerror.com/http/403-forbidden/ Severity: ERROR · Retryable: no · HTTP 403 · Layer: API Gateway Last verified: 2026-10-21 The server knows who you are but refuses the action: missing role, scope, IP allowlist or WAF block. Re-authenticating will not help; grant the permission or change the request. ### What you see ``` HTTP/1.1 403 Forbidden Content-Type: application/json {"error":"forbidden","message":"Missing scope: repo:write"} ``` ### Likely causes - **Missing role or OAuth scope** (High): The token is valid but was issued without the scope (e.g. repo:write) or the user lacks the role the endpoint requires. - **WAF, CDN or IP allowlist block** (Medium): Cloudflare, AWS WAF or nginx allow/deny rules reject the client IP or user agent before the app runs. - **CSRF or origin check failed** (Medium): Frameworks return 403 when the CSRF token or Origin header does not match for state-changing requests. ### Fixes #### Return 401 vs 403 deliberately (Node.js) Clients refresh tokens on 401 and show "no permission" on 403. Mixing them causes refresh loops. ```node.js app.delete('/projects/:id', async (req, res) => { if (!req.user) return res.status(401).set('WWW-Authenticate', 'Bearer').end(); if (req.user.role !== 'admin') { return res.status(403).json({ error: 'forbidden', required: 'admin' }); } // ...delete }); ``` #### Find who is blocking: app or edge (CLI) A cf-ray or server: cloudflare header means the edge blocked it; your app's request-id header means the app did. ```cli curl -si -X DELETE https://api.example.com/v1/projects/42 \ -H "Authorization: Bearer $TOKEN" | grep -iE '^(HTTP|server|cf-ray|x-amzn-requestid|x-request-id)' ``` ### FAQ **Is 403 retryable?** No. The same request will be rejected again until permissions or the request change. **Why does S3 return 403 for a missing object?** Without s3:ListBucket, S3 hides whether a key exists and returns AccessDenied (403) instead of NoSuchKey (404). **Should I return 403 or 404 for another tenant's resource?** 404 avoids leaking that the id exists. GitHub does this for private repos. ### Prevention - Include the missing permission in the 403 body. - Return 404 instead of 403 when existence itself is secret. - Log the policy decision with user id and resource. ## HTTP Status Codes Error 429: Too Many Requests URL: https://knowyourerror.com/http/429-too-many-requests/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: API Gateway Last verified: 2026-10-20 The client sent more requests than the server's rate limit allows in the window. Wait Retry-After seconds, then retry with exponential backoff and jitter. ### What you see ``` HTTP/1.1 429 Too Many Requests Retry-After: 30 RateLimit-Limit: 100 RateLimit-Remaining: 0 RateLimit-Reset: 30 Content-Type: application/json {"error":"rate_limited","message":"Rate limit exceeded: 100 requests per 60s"} ``` ### Likely causes - **Unbounded parallel requests** (High): Promise.all over hundreds of items or several workers sharing one API key exceed the per-key quota instantly. - **Retry storm after an outage** (Medium): Clients retry failed calls immediately and in sync, multiplying traffic right when the server recovers. - **Shared IP quota** (Low): Rate limits keyed on IP count all users behind a NAT or a serverless platform's egress as one client. ### Fixes #### Retry with Retry-After and jittered backoff (TypeScript) The busy loop makes the limit worse. Honoring Retry-After and adding jitter spreads retries out. ```typescript for (let attempt = 0; attempt < 6; attempt++) { const res = await fetch(url); if (res.status !== 429) return res; const ra = Number(res.headers.get('retry-after')); const base = Number.isFinite(ra) && ra > 0 ? ra * 1000 : 500 * 2 ** attempt; await new Promise(r => setTimeout(r, base + Math.random() * base * 0.2)); } throw new Error('rate limited after 6 attempts'); ``` #### Use urllib3 Retry with backoff in requests (Python) urllib3 sleeps for Retry-After when present and backs off exponentially otherwise. Only idempotent methods are retried. ```python import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry retry = Retry(total=5, backoff_factor=0.5, status_forcelist=[429, 503], respect_retry_after_header=True, allowed_methods=["GET", "PUT", "DELETE"]) s = requests.Session() s.mount("https://", HTTPAdapter(max_retries=retry)) r = s.get(url) ``` ### FAQ **Is Retry-After in seconds or a date?** Either. RFC 9110 allows delay-seconds or an HTTP-date; handle both. **Should I retry POST on 429?** Yes if the request was rejected before processing, which 429 implies, but send an idempotency key to be safe. **What is the difference between 429 and 503?** 429 blames the client's request rate. 503 means the server is overloaded or in maintenance for everyone. ### Prevention - Read RateLimit-Remaining and slow down before hitting 0. - Use a queue with fixed concurrency for bulk jobs. - Cache GET responses you request repeatedly. ## HTTP Status Codes Error 502: Bad Gateway URL: https://knowyourerror.com/http/502-bad-gateway/ Severity: CRITICAL · Retryable: yes · HTTP 502 · Layer: API Gateway Last verified: 2026-10-22 A proxy or load balancer got an invalid or no response from the upstream app (refused, reset, crashed). Check that the upstream is running on the expected host:port and that its keep-alive timeout exceeds the proxy's. ### What you see ``` HTTP/1.1 502 Bad Gateway Server: nginx/1.27.2 2026/10/22 13:07:44 [error] 31#31: *1842 connect() failed (111: Connection refused) while connecting to upstream, client: 172.18.0.1, server: api.example.com, request: "GET /v1/orders HTTP/1.1", upstream: "http://172.18.0.5:3000/v1/orders", host: "api.example.com" ``` ### Likely causes - **Upstream app down or wrong port** (High): The app crashed, is restarting, or listens on a different port than proxy_pass/target group points to. - **Keep-alive timeout race** (High): Node.js closes idle sockets after 5s by default while an ALB keeps them 60s. The LB reuses a socket the app just closed and gets a reset. - **Response headers too large** (Low): Big cookies or headers exceed proxy_buffer_size, so nginx rejects the upstream response as invalid. ### Fixes #### Set keepAliveTimeout above the load balancer idle timeout (Node.js) The upstream must never close an idle connection before the load balancer does, or in-flight reuse produces 502s. ```node.js const server = app.listen(3000, '0.0.0.0'); // ALB idle timeout is 60s: keep sockets open longer than that server.keepAliveTimeout = 65_000; server.headersTimeout = 66_000; ``` #### Confirm the upstream from inside the proxy (CLI) Testing from the proxy's network namespace shows whether the upstream is reachable, and the error log names the exact failure. ```cli docker compose exec nginx sh -c 'wget -qO- -S http://api:3000/healthz' tail -n 50 /var/log/nginx/error.log | grep upstream ``` ### FAQ **What is the difference between 502 and 504?** 502 means the upstream answered badly or refused. 504 means it did not answer before the proxy timeout. **Why do 502s spike during deploys?** Old instances are killed while the LB still routes to them. Use readiness checks and connection draining. **Is 502 safe to retry?** For idempotent requests, yes, once with backoff. For POSTs, use an idempotency key first. ### Prevention - Add a /healthz endpoint and an LB health check. - Graceful shutdown: stop accepting, drain, then exit. - Alert on upstream 5xx separately from app 5xx. ## HTTP Status Codes Error 504: Gateway Timeout URL: https://knowyourerror.com/http/504-gateway-timeout/ Severity: CRITICAL · Retryable: yes · HTTP 504 · Layer: API Gateway Last verified: 2026-10-22 The proxy waited longer than its timeout (nginx 60s, ALB 60s idle, API Gateway 29s) without a response from the upstream. Speed up the handler or make it async and return 202. ### What you see ``` HTTP/1.1 504 Gateway Time-out Server: nginx/1.27.2 2026/10/22 13:12:09 [error] 31#31: *2210 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 172.18.0.1, server: api.example.com, request: "POST /v1/reports HTTP/1.1", upstream: "http://172.18.0.5:3000/v1/reports" ``` ### Likely causes - **Slow query or external call** (High): The handler waits on an unindexed query or a slow third-party API longer than the proxy timeout. - **Synchronous long-running job** (Medium): Reports, exports or video processing done in the request path will always exceed gateway limits under load. - **Upstream saturated** (Medium): All workers are busy; requests queue in the app until the proxy gives up. ### Fixes #### Return 202 and process in the background (Node.js) The request finishes in milliseconds; clients poll or receive a webhook when the job is done. ```node.js app.post('/v1/reports', async (req, res) => { const job = await queue.add('report', req.body); res.status(202).location(`/v1/reports/${job.id}`).json({ id: job.id, status: 'queued' }); }); app.get('/v1/reports/:id', async (req, res) => { const job = await queue.getJob(req.params.id); res.json({ status: await job.getState(), url: job.returnvalue?.url }); }); ``` #### Align proxy timeouts with real latency (CLI) proxy_read_timeout defaults to 60s. Raise it only for routes that need it, after fixing the slow path. ```cli location /v1/ { proxy_pass http://api:3000; proxy_connect_timeout 5s; proxy_read_timeout 120s; # only for routes that truly need it proxy_send_timeout 120s; } ``` ### FAQ **What is the AWS API Gateway timeout?** REST and HTTP APIs have a 29-second integration timeout by default; REST APIs can request an increase for Regional and private endpoints. **Why does my request still complete after a 504?** The proxy gave up, but the upstream kept running. Make handlers idempotent so client retries do not duplicate work. **Is Cloudflare 524 the same as 504?** Similar: 524 is Cloudflare's 100-second origin response timeout. A 504 comes from your own proxy or load balancer. ### Prevention - Set app-level timeouts below the gateway timeout. - Measure p99 latency per route. - Use a job queue for anything > 10 s. ## HTTP Status Codes Error 400: Bad Request URL: https://knowyourerror.com/http/400-bad-request/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: API Gateway Last verified: 2026-10-08 The server could not parse the request: malformed JSON, wrong Content-Type, or invalid query syntax. Validate the body and send Content-Type: application/json. ### What you see ``` HTTP/1.1 400 Bad Request Content-Type: application/json {"error":"bad_request","message":"Unexpected token } in JSON at position 41"} ``` ## HTTP Status Codes Error 404: Not Found URL: https://knowyourerror.com/http/404-not-found/ Severity: WARNING · Retryable: no · HTTP 404 · Layer: API Gateway Last verified: 2026-10-07 No resource or route matches the URL. Check the path (plural vs singular, trailing slash, API version prefix) and that the id exists. ### What you see ``` HTTP/1.1 404 Not Found Content-Type: application/json {"error":"not_found","message":"No route for GET /v1/order/42"} ``` ## HTTP Status Codes Error 409: Conflict URL: https://knowyourerror.com/http/409-conflict/ Severity: WARNING · Retryable: no · HTTP 409 · Layer: API Gateway Last verified: 2026-10-09 The request conflicts with current state: duplicate unique value or a stale version (optimistic locking). Re-fetch the resource and resolve the conflict before retrying. ### What you see ``` HTTP/1.1 409 Conflict Content-Type: application/json {"error":"conflict","message":"A user with email ada@example.com already exists"} ``` ## HTTP Status Codes Error 422: Unprocessable Content URL: https://knowyourerror.com/http/422-unprocessable-content/ Severity: WARNING · Retryable: no · HTTP 422 · Layer: API Gateway Last verified: 2026-10-10 The body is well-formed but fails validation (missing field, wrong type, business rule). Read the field errors in the response and fix the payload. ### What you see ``` HTTP/1.1 422 Unprocessable Content Content-Type: application/json {"detail":[{"type":"missing","loc":["body","email"],"msg":"Field required","input":{"name":"Ada"}}]} ``` ## HTTP Status Codes Error 500: Internal Server Error URL: https://knowyourerror.com/http/500-internal-server-error/ Severity: CRITICAL · Retryable: yes · HTTP 500 · Layer: Runtime Last verified: 2026-10-06 An unhandled exception in the server. Look up the request id in server logs or your error tracker; the client cannot fix it. ### What you see ``` HTTP/1.1 500 Internal Server Error Content-Type: application/json {"error":"internal_server_error","requestId":"req_7f3c2a91"} ``` ## HTTP Status Codes Error 503: Service Unavailable URL: https://knowyourerror.com/http/503-service-unavailable/ Severity: CRITICAL · Retryable: yes · HTTP 503 · Layer: API Gateway Last verified: 2026-10-11 The server or load balancer has no healthy capacity (overload, maintenance, zero healthy targets). Retry after Retry-After; check health checks and autoscaling. ### What you see ``` HTTP/1.1 503 Service Unavailable Retry-After: 120 Content-Type: text/plain upstream connect error or disconnect/reset before headers. reset reason: connection failure ``` ## SMTP Error 550 5.7.26: Sender is unauthenticated URL: https://knowyourerror.com/smtp/550-5-7-26-unauthenticated-sender/ Severity: ERROR · Retryable: no · Layer: Network Last verified: 2026-10-20 Gmail rejected mail that passed neither SPF nor DKIM. Publish SPF for your sending IPs and sign with DKIM; bulk senders also need DMARC. ### What you see ``` 550-5.7.26 This mail has been blocked because the sender is 550-5.7.26 unauthenticated. Gmail requires all senders to authenticate with 550-5.7.26 either SPF or DKIM. 550 5.7.26 https://support.google.com/mail/answer/81126#authentication - gsmtp ``` ## SMTP Error 550 5.7.1: Unauthenticated email rejected URL: https://knowyourerror.com/smtp/550-5-7-1-unauthenticated-email/ Severity: ERROR · Retryable: no · Layer: Network Last verified: 2026-10-19 The receiving server rejected the message because it failed SPF/DKIM alignment and the From domain's DMARC policy is p=reject. Sign with DKIM for the From domain and include your sender in SPF. ### What you see ``` 550-5.7.1 Unauthenticated email from example.com is not accepted due to 550-5.7.1 domain's DMARC policy. Please contact the administrator of 550-5.7.1 example.com domain if this was a legitimate mail. To learn about 550-5.7.1 the DMARC initiative, go to 550 5.7.1 https://support.google.com/mail/?p=DmarcRejection d9443c01a7336-20e2b4c1a2fsi1234567b3.512 - gsmtp ``` ### Likely causes - **Sending service missing from SPF** (High): Mail goes out through a provider (SES, SendGrid, Resend) whose IPs are not in the domain's SPF record, so SPF fails. - **DKIM signs with a different domain** (High): The provider signs with its own domain (d=amazonses.com), which does not align with the From domain, so DMARC fails even if DKIM passes. - **Forwarding breaks SPF** (Low): A forwarder relays the message from its own IP; without ARC or aligned DKIM the final hop fails DMARC. ### Fixes #### Check and fix SPF, DKIM and DMARC DNS records (CLI) SPF must include every sending service. Start DMARC at quarantine with rua reports, confirm alignment, then move to reject. ```cli dig +short TXT example.com dig +short TXT resend._domainkey.example.com dig +short TXT _dmarc.example.com # records to publish example.com. TXT "v=spf1 mx include:amazonses.com -all" _dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com" ``` #### Sign outgoing mail with DKIM for the From domain (Node.js) A DKIM signature with d=example.com aligns with the From header, which is enough to pass DMARC even when SPF fails on forwarding. ```node.js const transport = nodemailer.createTransport({ host: 'smtp.example.com', port: 587, secure: false, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }, dkim: { domainName: 'example.com', keySelector: 's1', privateKey: process.env.DKIM_PRIVATE_KEY, }, }); await transport.sendMail({ from: 'no-reply@example.com', to, subject, html }); ``` ### FAQ **Why does Gmail now return 5.7.26 instead of 5.7.1?** Gmail moved most authentication failures to 5.7.26 in 2024. Older logs and other providers still use 5.7.1 for the same DMARC rejection. **Does 550 5.7.1 always mean DMARC?** No. Microsoft returns 550 5.7.1 for "Unable to relay" and blocklisted senders too. Read the text after the code. **How do I test SPF/DKIM/DMARC alignment?** Send to a Gmail address, open "Show original" and check that SPF, DKIM and DMARC all say PASS with your domain. ### Prevention - Use a custom MAIL FROM / return-path subdomain at your ESP. - Monitor DMARC aggregate (rua) reports weekly. - Send marketing and transactional mail from separate subdomains. ## SMTP Error 550 5.1.1: Recipient address rejected: user unknown URL: https://knowyourerror.com/smtp/550-5-1-1-user-unknown/ Severity: ERROR · Retryable: no · Layer: Network Last verified: 2026-10-17 The mailbox does not exist at the destination domain — a hard bounce. Stop sending to that address and add it to your suppression list; retrying hurts sender reputation. ### What you see ``` 550-5.1.1 The email account that you tried to reach does not exist. Please try 550-5.1.1 double-checking the recipient's email address for typos or 550-5.1.1 unnecessary spaces. For more information, go to 550 5.1.1 https://support.google.com/mail/?p=NoSuchUser 41be03b00d2f7-7ea9c6a1b2csi98765432a12.401 - gsmtp ``` ### Likely causes - **Typo in the address** (High): gmial.com, missing letters or a trailing space at signup. The domain may exist but the local part does not. - **Deleted or deactivated account** (Medium): Employees leave and corporate mailboxes are removed; old lists contain many such addresses. - **Catch-all disabled after migration** (Low): A domain moved providers and only some aliases were recreated. ### Fixes #### Suppress hard-bounced addresses (Node.js) Mailbox providers lower reputation for senders who keep mailing invalid addresses. Nodemailer exposes responseCode and response for SMTP errors. ```node.js for (const user of users) { if (await db.suppression.findUnique({ where: { email: user.email } })) continue; try { await sendEmail(user.email, digest); } catch (err) { if (err.responseCode === 550 && /5\.1\.1/.test(err.response)) { await db.suppression.create({ data: { email: user.email, reason: 'hard_bounce' } }); } else throw err; } } ``` #### Verify the domain has MX records at signup (TypeScript) Catches typo domains before the first send. Pair with double opt-in to confirm the mailbox itself exists. ```typescript import { resolveMx } from 'node:dns/promises'; export async function hasMx(email: string): Promise { const domain = email.split('@')[1]?.trim().toLowerCase(); if (!domain) return false; try { return (await resolveMx(domain)).length > 0; } catch { return false; } } ``` ### FAQ **Is 550 5.1.1 a hard or soft bounce?** Hard. The 5 class is permanent and x.1.1 means the mailbox does not exist; never retry it. **Can I verify an address exists before sending?** SMTP RCPT TO probing is unreliable and blocked by many providers. Use double opt-in or an email validation API. **Why does 5.1.1 appear for an address that worked yesterday?** The account was deleted or the domain changed providers. Treat it as invalid until the user updates it. ### Prevention - Use double opt-in for signups. - Process ESP bounce webhooks and suppress on hard bounce. - Keep hard-bounce rate under 2%. ## SMTP Error 421 4.7.0: Temporary system problem, try again later URL: https://knowyourerror.com/smtp/421-4-7-0-try-again-later/ Severity: WARNING · Retryable: yes · Layer: Network Last verified: 2026-10-12 The receiver temporarily deferred your connection for security or reputation reasons (also used for missing TLS or PTR). Your MTA should retry; fix PTR/rDNS and sending volume if it persists. ### What you see ``` 421-4.7.0 [203.0.113.25 15] Our system has detected an unusual rate of 421-4.7.0 unsolicited mail originating from your IP address. To protect our 421 4.7.0 users from spam, mail sent from your IP address has been temporarily rate limited. - gsmtp ``` ## SMTP Error 452 4.2.2: Mailbox full URL: https://knowyourerror.com/smtp/452-4-2-2-mailbox-full/ Severity: WARNING · Retryable: yes · Layer: Network Last verified: 2026-10-13 The recipient's mailbox is over quota. Soft bounce: retry later, and suppress the address if it keeps bouncing for several days. ### What you see ``` 452-4.2.2 The recipient's inbox is out of storage space. Please direct the 452-4.2.2 recipient to 452 4.2.2 https://support.google.com/mail/?p=OverQuotaTemp - gsmtp ``` ## SMTP Error 535 5.7.8: Authentication credentials invalid URL: https://knowyourerror.com/smtp/535-5-7-8-authentication-credentials-invalid/ Severity: ERROR · Retryable: no · Layer: Network Last verified: 2026-10-16 SMTP AUTH failed. For Gmail/Workspace use an App Password (2FA required); for ESPs use the SMTP-specific credentials, not your dashboard password. ### What you see ``` 535-5.7.8 Username and Password not accepted. For more information, go to 535 5.7.8 https://support.google.com/mail/?p=BadCredentials - gsmtp ``` ## SMTP Error 554 5.7.1: Relay access denied URL: https://knowyourerror.com/smtp/554-5-7-1-relay-access-denied/ Severity: ERROR · Retryable: no · Layer: Network Last verified: 2026-10-14 Postfix refused to relay to an external domain because the client did not authenticate. Connect on 587 with STARTTLS and SMTP AUTH, or add the client network to mynetworks. ### What you see ``` 554 5.7.1 : Relay access denied ``` ## SMTP Error 421 4.7.28: Rate limited: unusual rate of unsolicited mail URL: https://knowyourerror.com/smtp/421-4-7-28-rate-limited-unsolicited/ Severity: WARNING · Retryable: yes · Layer: Network Last verified: 2026-10-21 Gmail throttles your domain or IP because of spam complaints or a sudden volume spike. Slow down, warm up new IPs gradually and keep the spam rate below 0.3% in Postmaster Tools. ### What you see ``` 421-4.7.28 Gmail has detected an unusual rate of unsolicited mail from your 421-4.7.28 domain. To protect our users from spam, mail sent from your domain has 421 4.7.28 been temporarily rate limited. - gsmtp ``` ## AWS Error AccessDenied: S3 Access Denied URL: https://knowyourerror.com/aws/s3-accessdenied/ Severity: ERROR · Retryable: no · HTTP 403 · Layer: API Gateway Last verified: 2026-10-21 The caller's IAM policy, the bucket policy, a KMS key policy or Block Public Access denied the S3 action. Grant the action on the right ARN (bucket vs bucket/*) and check explicit Deny statements. ### What you see ``` An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/app-task-role/9f1c2e7a is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::acme-uploads/avatars/42.png" because no identity-based policy allows the s3:GetObject action ``` ### Likely causes - **Policy resource ARN mismatch** (High): Object actions (s3:GetObject, s3:PutObject) need arn:aws:s3:::bucket/*; bucket actions (s3:ListBucket) need arn:aws:s3:::bucket. Swapping them denies every call. - **Missing kms:Decrypt for SSE-KMS** (Medium): Objects encrypted with a customer-managed KMS key also require kms:Decrypt (and kms:GenerateDataKey for uploads) on that key. - **Missing key looks like 403** (Medium): Without s3:ListBucket, S3 returns AccessDenied instead of NoSuchKey for objects that do not exist. ### Fixes #### Grant object and bucket actions on the correct ARNs (JSON) Object-level actions only match object ARNs. Adding ListBucket also turns misleading 403s for missing keys into 404 NoSuchKey. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::acme-uploads" }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::acme-uploads/*" } ] } ``` #### Confirm the identity and simulate the policy (CLI) get-caller-identity reveals which role is really used. The simulator reports allowed, implicitDeny or explicitDeny and the matching statement (identity policies only). ```cli aws sts get-caller-identity aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:role/app-task-role \ --action-names s3:GetObject \ --resource-arns arn:aws:s3:::acme-uploads/avatars/42.png ``` ### FAQ **Why do I get AccessDenied on an object that does not exist?** S3 returns 403 instead of 404 when you lack s3:ListBucket, to avoid revealing which keys exist. **My IAM policy allows it — why still denied?** An explicit Deny anywhere (bucket policy, SCP, permission boundary, VPC endpoint policy) wins over Allow. KMS key policies can also block. **Does Block Public Access cause AccessDenied?** Yes, for anonymous requests and when putting public ACLs or policies. Use CloudFront with OAC instead of public buckets. ### Prevention - Check bucket policy and SCPs for explicit Deny. - Use IAM Access Analyzer to validate policies. - Enable CloudTrail data events to see denied calls. ## AWS Error NoSuchKey: S3 NoSuchKey URL: https://knowyourerror.com/aws/s3-nosuchkey/ Severity: WARNING · Retryable: no · HTTP 404 · Layer: API Gateway Last verified: 2026-10-18 No object exists at that exact key in the bucket. Check for a leading slash, URL-encoded characters and case, and handle NoSuchKey as a 404 in code. ### What you see ``` NoSuchKey: The specified key does not exist. at de_NoSuchKeyRes (/app/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4908:21) '$fault': 'client', '$metadata': { httpStatusCode: 404, requestId: 'R7X1N9M2Q4K8P0ZT', attempts: 1 }, Code: 'NoSuchKey', Key: 'uploads/2026/10/report final.pdf' ``` ### Likely causes - **Key built with wrong encoding** (High): Passing a URL-encoded key (report%20final.pdf) or a + for spaces to the SDK, which encodes again. S3 keys are literal strings. - **Leading slash or wrong prefix** (Medium): /uploads/a.png and uploads/a.png are different keys. Path joins often add the slash. - **Read before write completes** (Low): Reading right after an async upload that has not finished, or from the wrong region/bucket. ### Fixes #### Catch NoSuchKey and return 404 (TypeScript) SDK v3 exports NoSuchKey as a typed exception class, so instanceof is reliable. ```typescript import { GetObjectCommand, NoSuchKey } from '@aws-sdk/client-s3'; try { const obj = await s3.send(new GetObjectCommand({ Bucket, Key })); return new Response(obj.Body!.transformToWebStream()); } catch (err) { if (err instanceof NoSuchKey) return new Response('Not found', { status: 404 }); throw err; } ``` #### Normalize keys before calling S3 (TypeScript) S3 keys are raw UTF-8 strings without a leading slash; the SDK handles URL encoding itself. ```typescript const Key = decodeURIComponent(path).replace(/^\/+/, ''); // verify with: aws s3api head-object --bucket $BUCKET --key "$KEY" ``` ### FAQ **Why do I get AccessDenied instead of NoSuchKey?** The caller lacks s3:ListBucket on the bucket, so S3 hides the difference. Add ListBucket to get 404s. **Is S3 eventually consistent for new objects?** No, since December 2020 S3 provides strong read-after-write consistency for all PUTs and DELETEs. **How do I list keys under a prefix?** aws s3api list-objects-v2 --bucket my-bucket --prefix uploads/2026/ --max-items 20. ### Prevention - Store the exact key returned at upload time. - Use HeadObject to check existence cheaply. - Grant s3:ListBucket so missing keys return 404, not 403. ## AWS Error TooManyRequestsException: Lambda Rate Exceeded (throttled) URL: https://knowyourerror.com/aws/lambda-toomanyrequestsexception/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: Runtime Last verified: 2026-10-17 Lambda throttled the invoke because the function's reserved concurrency or the account's regional concurrency limit (default 1,000) is used up. Raise reserved concurrency or the quota, and retry with backoff. ### What you see ``` An error occurred (TooManyRequestsException) when calling the Invoke operation (reached max retries: 2): Rate Exceeded. { "Reason": "ReservedFunctionConcurrentInvocationLimitExceeded", "Type": "User", "message": "Rate Exceeded." } ``` ### Likely causes - **Reserved concurrency set too low** (High): A function with reserved concurrency 10 throttles the 11th concurrent invoke (Reason ReservedFunctionConcurrentInvocationLimitExceeded). - **Account concurrency exhausted** (Medium): All functions in the region share the account limit. One noisy function can starve others (Reason ConcurrentInvocationLimitExceeded). - **Burst scaling limit** (Low): Each function can add at most 1,000 concurrent executions every 10 seconds; sudden spikes beyond that throttle. ### Fixes #### Inspect and raise concurrency (CLI) Shows whether a per-function reservation or the account limit is the ceiling. Request a quota increase in Service Quotas if the account limit is too low. ```cli aws lambda get-function-concurrency --function-name image-resize aws lambda get-account-settings --query 'AccountLimit.ConcurrentExecutions' aws lambda put-function-concurrency --function-name image-resize \ --reserved-concurrent-executions 200 ``` #### Use adaptive retries in the SDK (TypeScript) Adaptive mode adds client-side rate limiting on throttles. Async invocation lets Lambda buffer bursts instead of failing the caller. ```typescript import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda'; const lambda = new LambdaClient({ maxAttempts: 6, retryMode: 'adaptive' }); await lambda.send(new InvokeCommand({ FunctionName: 'image-resize', InvocationType: 'Event', // async: Lambda queues and retries throttles for up to 6h Payload: Buffer.from(JSON.stringify({ key })), })); ``` ### FAQ **What is the default Lambda concurrency limit?** 1,000 concurrent executions per Region per account, shared by all functions. New accounts may start lower. **Do async invocations fail on throttle?** No. Lambda retries throttled async events for up to 6 hours, then sends them to a DLQ or on-failure destination if configured. **Why is reserved concurrency 0 throttling everything?** Setting reserved concurrency to 0 disables the function. Use delete-function-concurrency to remove the reservation. ### Prevention - Put SQS in front of bursty Lambda workloads. - Alarm on the Throttles CloudWatch metric. - Set SQS event source maximum concurrency below reserved concurrency. ## EVM / Solidity Error 0x08c379a0: Error(string) revert URL: https://knowyourerror.com/evm/0x08c379a0-error-string-revert/ Severity: ERROR · Retryable: no · Layer: Smart Contract Last verified: 2026-10-20 0x08c379a0 is the selector of Error(string), emitted by require(cond, "msg") and revert("msg"). ABI-decode the rest of the data to read the reason string; fix the failing condition. ### What you see ``` ContractFunctionExecutionError: The contract function "transfer" reverted with the following reason: ERC20: transfer amount exceeds balance Contract Call: address: 0x5FbDB2315678afecb367f032d93F642f64180aa3 function: transfer(address to, uint256 amount) args: (0x70997970C51812dc3A010C7d01b50e0d17dc79C8, 1000000000000000000000) sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Raw revert data: 0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002645524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63650000000000000000000000000000000000000000000000000000 Version: viem@2.21.37 ``` ### Likely causes - **require() condition failed** (High): A require(cond, "reason") evaluated false — balance, allowance, deadline or ownership check. The reason string is ABI-encoded after the selector. - **Pre-v5 OpenZeppelin string errors** (Medium): OpenZeppelin 4.x contracts revert with strings like "ERC20: transfer amount exceeds balance"; v5 switched to custom errors. - **Wrong msg.sender or value in simulation** (Low): eth_call without from, or with value 0, hits a guard that would pass for the real sender. ### Fixes #### Replace require strings with custom errors (Solidity) Custom errors are cheaper to deploy and revert, and carry typed values. call{value:} avoids the 2300-gas stipend of transfer. ```solidity error InsufficientBalance(uint256 available, uint256 required); function withdraw(uint256 amount) external { uint256 bal = balances[msg.sender]; if (bal < amount) revert InsufficientBalance(bal, amount); balances[msg.sender] = bal - amount; (bool ok, ) = payable(msg.sender).call{value: amount}(""); require(ok); } ``` #### Simulate first and read the reason (viem) (TypeScript) simulateContract runs eth_call first, so you get the decoded reason without paying gas for a failed transaction. ```typescript import { BaseError, ContractFunctionRevertedError } from 'viem'; try { const { request } = await client.simulateContract({ account, address, abi, functionName: 'transfer', args: [to, amount], }); await wallet.writeContract(request); } catch (err) { const revert = err instanceof BaseError ? err.walk(e => e instanceof ContractFunctionRevertedError) : null; if (revert instanceof ContractFunctionRevertedError) console.log(revert.reason); throw err; } ``` ### FAQ **How do I decode 0x08c379a0 revert data by hand?** Strip the 4-byte selector and ABI-decode the remainder as (string). cast decode-error --sig "Error(string)" does it in one step. **Why does ethers v6 show "execution reverted" with no reason?** The RPC did not return revert data (some providers strip it on estimateGas). Retry with eth_call or a provider that returns data. **What is the gas cost difference vs custom errors?** Custom errors save deployment size and a few hundred gas per revert because the string is not stored or ABI-encoded. ### Prevention - Always simulate with the real from address. - Prefer custom errors in new contracts. - Decode raw data with cast decode-error. ## AWS Error ExpiredToken: Security token expired URL: https://knowyourerror.com/aws/sts-expiredtoken/ Severity: ERROR · Retryable: no · HTTP 403 · Layer: API Gateway Last verified: 2026-10-16 The temporary credentials (AWS_SESSION_TOKEN from SSO, AssumeRole or MFA) passed their expiration. Re-authenticate with aws sso login, or use a credential provider that refreshes automatically. ### What you see ``` An error occurred (ExpiredToken) when calling the GetCallerIdentity operation: The security token included in the request is expired ``` ### Likely causes - **Stale env vars from an old session** (High): AWS_ACCESS_KEY_ID / AWS_SESSION_TOKEN were exported hours ago; env vars override the profile and never refresh. - **SSO session expired** (High): IAM Identity Center sessions last 1–12 hours. After that every call fails until aws sso login. - **Long jobs outlive AssumeRole** (Medium): AssumeRole credentials default to 1 hour; a batch job that fetched them once fails mid-run. ### Fixes #### Clear stale env vars and log in again (CLI) Environment variables take precedence over ~/.aws/config. Removing them lets the SSO profile supply fresh, auto-cached credentials. ```cli unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN aws sso login --profile dev aws sts get-caller-identity --profile dev ``` #### Let the SDK refresh credentials (TypeScript) Credential providers re-assume the role before expiry. Static objects never refresh. ```typescript import { S3Client } from '@aws-sdk/client-s3'; import { fromTemporaryCredentials } from '@aws-sdk/credential-providers'; const s3 = new S3Client({ credentials: fromTemporaryCredentials({ params: { RoleArn: 'arn:aws:iam::123456789012:role/batch', DurationSeconds: 3600 }, }), }); ``` ### FAQ **ExpiredToken vs InvalidClientTokenId — what is the difference?** ExpiredToken means the token was valid but timed out. InvalidClientTokenId means the access key itself is unknown or deleted. **How long do AssumeRole credentials last?** 1 hour by default, up to the role's MaxSessionDuration (max 12 hours). Role chaining caps it at 1 hour. **Why does Terraform fail with ExpiredToken mid-apply?** Its credentials were resolved at start. Use an SSO profile with a long session or run with a role that lasts the whole apply. ### Prevention - Never paste session tokens into .env files. - Use IAM roles for EC2/ECS/Lambda; the SDK refreshes them. - Raise MaxSessionDuration for long batch roles (up to 12h). ## AWS Error SlowDown: S3 SlowDown URL: https://knowyourerror.com/aws/s3-slowdown/ Severity: WARNING · Retryable: yes · HTTP 503 · Layer: API Gateway Last verified: 2026-10-09 You exceeded S3's per-prefix request rate (3,500 writes / 5,500 reads per second per prefix). Spread keys across more prefixes and retry with backoff. ### What you see ``` An error occurred (SlowDown) when calling the PutObject operation (reached max retries: 4): Please reduce your request rate. ``` ## AWS Error NoSuchBucket: S3 NoSuchBucket URL: https://knowyourerror.com/aws/s3-nosuchbucket/ Severity: ERROR · Retryable: no · HTTP 404 · Layer: API Gateway Last verified: 2026-10-08 The bucket name is wrong, was deleted, or you are using a custom endpoint (MinIO, R2) without setting endpoint and forcePathStyle. Check the name and endpoint configuration. ### What you see ``` An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation: The specified bucket does not exist ``` ## AWS Error InvalidClientTokenId: Invalid client token id URL: https://knowyourerror.com/aws/sts-invalidclienttokenid/ Severity: ERROR · Retryable: no · HTTP 403 · Layer: API Gateway Last verified: 2026-10-13 The access key id is unknown, deactivated, or temporary credentials are sent without their session token (or to a Region not enabled for the account). Rotate keys and check which profile is active. ### What you see ``` An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid. ``` ## AWS Error ResourceNotFoundException: Lambda function not found URL: https://knowyourerror.com/aws/lambda-resourcenotfoundexception/ Severity: ERROR · Retryable: no · HTTP 404 · Layer: API Gateway Last verified: 2026-10-10 The function, alias or version does not exist in that Region/account. Check AWS_REGION, the alias name and that the deploy finished. ### What you see ``` An error occurred (ResourceNotFoundException) when calling the Invoke operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:image-resize:prod ``` ## AWS Error AccessDeniedException: Access denied (JSON-protocol services) URL: https://knowyourerror.com/aws/accessdeniedexception/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: API Gateway Last verified: 2026-10-15 IAM denied the action for this principal (Secrets Manager, DynamoDB, KMS, Lambda and other JSON-protocol services use this name). Add the exact action and resource ARN to the role policy. ### What you see ``` An error occurred (AccessDeniedException) when calling the GetSecretValue operation: User: arn:aws:sts::123456789012:assumed-role/app-task-role/9f1c2e7a is not authorized to perform: secretsmanager:GetSecretValue on resource: prod/db because no identity-based policy allows the secretsmanager:GetSecretValue action ``` ## EVM / Solidity Error 0x4e487b71: Panic(0x11): arithmetic overflow/underflow URL: https://knowyourerror.com/evm/0x4e487b71-panic-arithmetic-overflow/ Severity: CRITICAL · Retryable: no · Layer: Smart Contract Last verified: 2026-10-19 0x4e487b71 is Panic(uint256); code 0x11 means checked arithmetic over- or underflowed (Solidity ≥0.8). Usually a subtraction where b > a. Validate inputs and revert with a custom error first. ### What you see ``` ContractFunctionExecutionError: The contract function "claim" reverted with the following reason: Arithmetic operation resulted in underflow or overflow. Contract Call: address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 function: claim(uint256 amount) args: (5000000000000000000) Raw revert data: 0x4e487b710000000000000000000000000000000000000000000000000000000000000011 Version: viem@2.21.37 ``` ### Likely causes - **Subtracting a larger value** (High): balance - amount or end - start where the right side is bigger. 0.8.x checked math reverts with Panic(0x11) instead of wrapping. - **Overflow in multiplication** (Medium): amount * price * 1e18 exceeds 2^256-1 for large inputs, or a downcast like uint128(x) overflows via explicit math. - **Decrementing a zero counter** (Medium): totalStaked-- or count -= 1 after state was already cleared by another path. ### Fixes #### Check bounds and revert with a clear error (Solidity) The explicit check gives callers a decodable reason. unchecked is safe after the check and saves gas. ```solidity error ClaimExceedsRewards(uint256 available, uint256 requested); function claim(uint256 amount) external { uint256 available = rewards[msg.sender]; if (amount > available) revert ClaimExceedsRewards(available, amount); unchecked { rewards[msg.sender] = available - amount; } token.safeTransfer(msg.sender, amount); } ``` #### Use mulDiv for large products (Solidity) Math.mulDiv computes a*b/c with full 512-bit intermediate precision, so the product cannot overflow. ```solidity import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; uint256 shares = Math.mulDiv(assets, totalSupply, totalAssets); ``` ### FAQ **What do the other Panic codes mean?** 0x01 assert failed, 0x12 division by zero, 0x21 invalid enum, 0x31 pop on empty array, 0x32 index out of bounds, 0x41 out of memory, 0x51 uninitialized function pointer. **Why no overflow before Solidity 0.8?** Earlier versions wrapped silently, which is why SafeMath existed. 0.8.0 made checked arithmetic the default. **Does a Panic consume all gas?** No. Since 0.8.0 Panic uses the REVERT opcode and refunds remaining gas; only pre-0.8 assert used INVALID and consumed all gas. ### Prevention - Fuzz arithmetic with forge test --fuzz-runs 10000. - Use SafeCast for downcasts. - Treat any Panic in production as a bug, not a user error. ## EVM / Solidity Error 0xe450d38c: ERC20InsufficientBalance URL: https://knowyourerror.com/evm/0xe450d38c-erc20-insufficient-balance/ Severity: ERROR · Retryable: no · Layer: Smart Contract Last verified: 2026-10-22 An OpenZeppelin v5 ERC-20 transfer or burn needed more tokens than the sender holds. The data decodes to (sender, balance, needed); top up the balance or lower the amount, minding decimals. ### What you see ``` Error: execution reverted (unknown custom error) (action="estimateGas", data="0xe450d38c00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000000000000000000000000000d8d726b7177a8000000000000000000000000000000000000000000000000003635c9adc5dea00000", reason=null, transaction={ "data": "0xa9059cbb...", "from": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "to": "0x5FbDB2315678afecb367f032d93F642f64180aa3" }, invocation=null, revert=null, code=CALL_EXCEPTION, version=6.13.4) ``` ### Likely causes - **Amount not scaled by decimals** (High): Passing 1000 * 10**18 to a 6-decimal token (USDC) asks for 10^12 times too much. Always read decimals(). - **Wrong sender in transferFrom** (Medium): transferFrom checks the from address's balance, not the caller's. The vault or user you pull from is empty. - **Fee-on-transfer or rebasing token** (Low): The received amount was less than expected, so a follow-up transfer of the full amount exceeds the actual balance. ### Fixes #### Decode the custom error with the ABI (ethers v6) (TypeScript) Including the error in the ABI turns "unknown custom error" into named args. parseUnits with on-chain decimals avoids scale bugs. ```typescript import { Contract, Interface, parseUnits } from 'ethers'; const iface = new Interface([ 'error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)', ]); const decimals = await token.decimals(); const amount = parseUnits('250', decimals); try { await token.transfer.staticCall(to, amount); } catch (e: any) { const parsed = iface.parseError(e.data); if (parsed?.name === 'ERC20InsufficientBalance') console.log(parsed.args.balance, parsed.args.needed); } ``` #### Check balance before transferring (Solidity) An early check yields your own clear error; measuring received handles fee-on-transfer tokens. ```solidity using SafeERC20 for IERC20; uint256 bal = IERC20(token).balanceOf(user); if (bal < amount) revert NotEnoughTokens(bal, amount); uint256 before = IERC20(token).balanceOf(address(this)); IERC20(token).safeTransferFrom(user, address(this), amount); uint256 received = IERC20(token).balanceOf(address(this)) - before; ``` ### FAQ **How do I decode 0xe450d38c with cast?** cast decode-error --sig "ERC20InsufficientBalance(address,uint256,uint256)" prints sender, balance and needed. **Which OpenZeppelin version uses this error?** OpenZeppelin Contracts v5.0+ (IERC20Errors, ERC-6093). v4 reverted with "ERC20: transfer amount exceeds balance". **Is this the same as insufficient allowance?** No. Allowance failures use ERC20InsufficientAllowance (0xfb8f41b2) and are fixed with approve(). ### Prevention - Add OZ IERC20Errors to your frontend ABI. - Never hardcode 18 decimals. - Show balance vs needed in the UI before signing. ## EVM / Solidity Error 0x118cdaa7: OwnableUnauthorizedAccount URL: https://knowyourerror.com/evm/0x118cdaa7-ownable-unauthorized-account/ Severity: ERROR · Retryable: no · Layer: Smart Contract Last verified: 2026-10-18 An onlyOwner function was called by an address that is not owner(). The data contains the caller; send from the owner account, or transfer ownership — and remember v5 requires an initialOwner in the constructor. ### What you see ``` ContractFunctionExecutionError: The contract function "setFee" reverted. Error: OwnableUnauthorizedAccount(address account) (0x70997970C51812dc3A010C7d01b50e0d17dc79C8) Raw revert data: 0x118cdaa700000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8 Version: viem@2.21.37 ``` ### Likely causes - **Calling from the wrong account** (High): The script or wallet uses a different signer than the deployer that owns the contract. - **Owner set to the factory or deployer contract** (Medium): With OZ v5 Ownable(initialOwner), passing msg.sender inside a factory makes the factory the owner, not the EOA. - **Ownership already transferred or renounced** (Medium): transferOwnership to a multisig or renounceOwnership() means the old key can no longer call admin functions. ### Fixes #### Pass the intended owner explicitly (OZ v5) (Solidity) An explicit initialOwner makes ownership deterministic across factories and scripts. Ownable2Step requires the new owner to accept, avoiding typos. ```solidity contract Vault is Ownable2Step { constructor(address initialOwner) Ownable(initialOwner) {} } // factory: new Vault(msg.sender); ``` #### Check owner() and use that signer (viem) (TypeScript) Failing fast with both addresses is clearer than an on-chain revert and saves gas. ```typescript const owner = await client.readContract({ address, abi, functionName: 'owner' }); if (owner.toLowerCase() !== wallet.account.address.toLowerCase()) { throw new Error(`Signer ${wallet.account.address} is not owner ${owner}`); } const { request } = await client.simulateContract({ account: wallet.account, address, abi, functionName: 'setFee', args: [30n], }); await wallet.writeContract(request); ``` ### FAQ **How do I see who the owner is?** cast call "owner()(address)" --rpc-url $RPC_URL returns the current owner. **Why does OZ v5 Ownable require a constructor argument?** v5 removed the implicit msg.sender owner to prevent factory and proxy ownership mistakes. Pass initialOwner explicitly. **What about AccessControl roles?** AccessControl reverts with AccessControlUnauthorizedAccount(account, role), selector 0xe2517d3f. Grant the role with grantRole. ### Prevention - Use a multisig as owner in production. - Prefer Ownable2Step for transfers. - Log owner() in deploy scripts after deployment. ## EVM / Solidity Error 0xfb8f41b2: ERC20InsufficientAllowance URL: https://knowyourerror.com/evm/0xfb8f41b2-erc20-insufficient-allowance/ Severity: ERROR · Retryable: no · Layer: Smart Contract Last verified: 2026-10-15 transferFrom was called by a spender whose allowance is lower than the amount. The data decodes to (spender, allowance, needed); call approve(spender, amount) from the token owner first. ### What you see ``` Error: execution reverted (unknown custom error) (action="estimateGas", data="0xfb8f41b2000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee6b280", reason=null, code=CALL_EXCEPTION, version=6.13.4) ``` ## EVM / Solidity Error 0x7e273289: ERC721NonexistentToken URL: https://knowyourerror.com/evm/0x7e273289-erc721-nonexistent-token/ Severity: ERROR · Retryable: no · Layer: Smart Contract Last verified: 2026-10-11 The tokenId was never minted or has been burned (OpenZeppelin v5 ERC721). Check totalSupply / mint events, and remember collections may start at id 0 or 1. ### What you see ``` ContractFunctionExecutionError: The contract function "ownerOf" reverted. Error: ERC721NonexistentToken(uint256 tokenId) (10001) ``` ## EVM / Solidity Error 0xd93c0665: EnforcedPause URL: https://knowyourerror.com/evm/0xd93c0665-enforced-pause/ Severity: WARNING · Retryable: yes · Layer: Smart Contract Last verified: 2026-10-14 The contract is paused (OpenZeppelin v5 Pausable) and the function has whenNotPaused. Wait for the admin to unpause; check with paused(). ### What you see ``` ContractFunctionExecutionError: The contract function "deposit" reverted. Error: EnforcedPause() Raw revert data: 0xd93c0665 ``` ## EVM / Solidity Error 0x3ee5aeb5: ReentrancyGuardReentrantCall URL: https://knowyourerror.com/evm/0x3ee5aeb5-reentrancy-guard-reentrant-call/ Severity: CRITICAL · Retryable: no · Layer: Smart Contract Last verified: 2026-10-12 A nonReentrant function was entered again before the first call finished, typically via a token or ETH callback. Expected for attacks; for legit flows, split the call or move the external call last. ### What you see ``` ContractFunctionExecutionError: The contract function "withdraw" reverted. Error: ReentrancyGuardReentrantCall() Raw revert data: 0x3ee5aeb5 ``` ## EVM / Solidity Error 0xe2517d3f: AccessControlUnauthorizedAccount URL: https://knowyourerror.com/evm/0xe2517d3f-access-control-unauthorized-account/ Severity: ERROR · Retryable: no · Layer: Smart Contract Last verified: 2026-10-16 The caller lacks the required role (here MINTER_ROLE = keccak256("MINTER_ROLE")). Grant it with grantRole(role, account) from an admin of that role. ### What you see ``` ContractFunctionExecutionError: The contract function "mint" reverted. Error: AccessControlUnauthorizedAccount(address account, bytes32 neededRole) (0x70997970C51812dc3A010C7d01b50e0d17dc79C8, 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6) ``` ## Node.js Error ECONNREFUSED: Connection refused URL: https://knowyourerror.com/node/econnrefused/ Severity: ERROR · Retryable: yes · Layer: Network Last verified: 2026-10-22 The target host answered with a TCP RST: nothing is listening on that address and port. Start the service, fix the host/port, and on Node 17+ use 127.0.0.1 instead of localhost if the server only binds IPv4. ### What you see ``` Error: connect ECONNREFUSED ::1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1607:16) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '::1', port: 5432 } ``` ### Likely causes - **localhost resolves to IPv6 ::1** (High): Since Node 17, DNS results keep OS order, so localhost often becomes ::1. A server bound only to 127.0.0.1 refuses the IPv6 connection. - **Service not running or wrong port** (High): The database or API has not started yet, crashed, or listens on a different port than configured. - **localhost inside a container** (Medium): In Docker, localhost is the container itself; the database runs in another container reachable by service name. ### Fixes #### Connect to an explicit IPv4 address or prefer IPv4 (Node.js) Using 127.0.0.1, or preferring IPv4 results, matches servers that only listen on IPv4. ```node.js import dns from 'node:dns'; dns.setDefaultResultOrder('ipv4first'); // or run node --dns-result-order=ipv4first const client = new pg.Client({ host: process.env.PGHOST ?? '127.0.0.1', port: 5432 }); ``` #### Verify something is listening before blaming the app (CLI) lsof shows whether the server binds 127.0.0.1, ::1 or *. nc confirms which address actually accepts connections. ```cli # who listens on 5432, and on which address family? lsof -nP -iTCP:5432 -sTCP:LISTEN nc -vz 127.0.0.1 5432 nc -vz ::1 5432 ``` ### FAQ **Why did ECONNREFUSED start after upgrading to Node 18?** Node 17 stopped reordering DNS results to IPv4 first. localhost now resolves to ::1 on many systems; use 127.0.0.1 or --dns-result-order=ipv4first. **ECONNREFUSED vs ETIMEDOUT?** ECONNREFUSED means the host actively rejected the connection (nothing listening). ETIMEDOUT means no reply at all, usually a firewall or wrong IP. **What does errno -111 vs -61 mean?** Both are ECONNREFUSED: -111 on Linux, -61 on macOS. The numeric errno is platform-specific; use err.code. ### Prevention - Wait for dependencies with healthchecks, not sleep. - Read host/port from env, never hardcode localhost. - Retry startup connections with backoff. ## Node.js Error EADDRINUSE: Address already in use URL: https://knowyourerror.com/node/eaddrinuse/ Severity: ERROR · Retryable: no · Layer: Runtime Last verified: 2026-10-20 Another process already listens on that port, often a previous dev server that did not exit. Kill it (lsof -ti :3000 | xargs kill) or use a different PORT, and close the server on SIGTERM. ### What you see ``` node:events:497 throw er; // Unhandled 'error' event ^ Error: listen EADDRINUSE: address already in use :::3000 at Server.setupListenHandle [as _listen2] (node:net:1904:16) at listenInCluster (node:net:1952:12) at Server.listen (node:net:2063:7) { code: 'EADDRINUSE', errno: -98, syscall: 'listen', address: '::', port: 3000 } ``` ### Likely causes - **Orphaned previous dev server** (High): A crashed terminal, nodemon restart or IDE run left the old process alive and holding the port. - **listen() called twice** (Medium): Importing the app module twice, or calling app.listen in both server.ts and a test, binds the same port twice in one process. - **Another service owns the port** (Medium): macOS AirPlay Receiver uses 5000 and 7000; Docker containers publish ports on the host. ### Fixes #### Find and stop the process holding the port (CLI) lsof shows the PID and command name so you do not kill the wrong process. ```cli lsof -nP -iTCP:3000 -sTCP:LISTEN kill $(lsof -ti tcp:3000) # SIGTERM; use kill -9 only if it ignores it # Windows: netstat -ano | findstr :3000 then taskkill /PID /F ``` #### Close the server on shutdown and export app without listening (Node.js) Tests can import app without binding a port, and a clean close releases the socket on restart. ```node.js // app.js const app = express(); module.exports = app; // server.js const server = require('./app').listen(process.env.PORT ?? 3000); for (const sig of ['SIGINT', 'SIGTERM']) { process.on(sig, () => server.close(() => process.exit(0))); } ``` ### FAQ **Why is port 5000 taken on my Mac?** macOS Monterey+ runs AirPlay Receiver on 5000 and 7000. Disable it in System Settings > General > AirDrop & Handoff or use another port. **Can I make the OS pick a free port?** Yes, listen(0) binds a random free port; read it with server.address().port. **What does ::: in "address already in use :::3000" mean?** It is the IPv6 wildcard :: followed by :3000, meaning Node tried to listen on all interfaces. ### Prevention - Read PORT from env. - Use supertest(app) instead of listening in tests. - Handle SIGTERM in every long-running process. ## Node.js Error ERR_REQUIRE_ESM: require() of ES Module not supported URL: https://knowyourerror.com/node/err-require-esm/ Severity: ERROR · Retryable: no · Layer: Runtime Last verified: 2026-10-21 CommonJS code called require() on an ESM-only package (node-fetch 3, chalk 5, nanoid 4…). Use await import(), switch your project to ESM, pin the last CJS version, or upgrade to Node ≥20.19 / 22.12 where require(esm) works. ### What you see ``` Error [ERR_REQUIRE_ESM]: require() of ES Module /app/node_modules/node-fetch/src/index.js from /app/index.js not supported. Instead change the require of /app/node_modules/node-fetch/src/index.js in /app/index.js to a dynamic import() which is available in all CommonJS modules. at Object. (/app/index.js:1:15) { code: 'ERR_REQUIRE_ESM' } ``` ### Likely causes - **Dependency went ESM-only** (High): A major version bump (e.g. node-fetch 2 → 3) removed the CommonJS build. Your require() call is unchanged. - **TypeScript compiles imports to require** (High): "module": "commonjs" in tsconfig turns import x from 'esm-pkg' into require(), which fails at runtime. - **Old Node version** (Medium): Node before 20.19 / 22.12 cannot require() ES modules at all. Newer versions only fail for modules with top-level await (ERR_REQUIRE_ASYNC_MODULE). ### Fixes #### Use dynamic import() from CommonJS (Node.js) import() is allowed in CJS and loads ESM asynchronously. On Node 18+, the built-in fetch removes the need for node-fetch. ```node.js async function getJson(url) { const { default: fetch } = await import('node-fetch'); const res = await fetch(url); return res.json(); } // or drop the dependency: global fetch is built into Node 18+ ``` #### Compile to ESM with NodeNext (TypeScript) NodeNext emits native import statements for "type": "module" packages. Relative imports then need .js extensions. ```typescript // package.json { "type": "module" } // tsconfig.json { "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "target": "es2022", "outDir": "dist" } } ``` ### FAQ **Does Node 22 still throw ERR_REQUIRE_ESM?** From 22.12 (and 20.19) require() of synchronous ES modules is enabled by default. Modules with top-level await throw ERR_REQUIRE_ASYNC_MODULE instead. **Can I just downgrade the package?** Yes: node-fetch@2, chalk@4 and nanoid@3 still ship CommonJS. It is a quick fix but you miss future updates. **Why does Jest throw this but node does not?** Jest uses its own CJS module system. Configure transformIgnorePatterns to transpile the ESM package, or use Vitest. ### Prevention - Check a package's "exports" and "type" before major upgrades. - Prefer ESM for new Node projects. - Upgrade to Node 22 LTS for require(esm). ## Node.js Error ENOENT: No such file or directory URL: https://knowyourerror.com/node/enoent/ Severity: ERROR · Retryable: no · Layer: Runtime Last verified: 2026-10-10 The path does not exist relative to process.cwd(), not to the source file. Resolve paths with path.join(__dirname, ...) or new URL('./file', import.meta.url). ### What you see ``` Error: ENOENT: no such file or directory, open './config/settings.json' at Object.openSync (node:fs:573:18) at Object.readFileSync (node:fs:452:35) { errno: -2, code: 'ENOENT', syscall: 'open', path: './config/settings.json' } ``` ## Node.js Error ETIMEDOUT: Connection timed out URL: https://knowyourerror.com/node/etimedout/ Severity: ERROR · Retryable: yes · Layer: Network Last verified: 2026-10-12 No SYN-ACK came back before the OS timeout: firewall/security group dropping packets, wrong IP, or no route (private IP from a public network). Check reachability with nc -vz. ### What you see ``` Error: connect ETIMEDOUT 10.0.3.17:443 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1607:16) { errno: -110, code: 'ETIMEDOUT', syscall: 'connect', address: '10.0.3.17', port: 443 } ``` ## Node.js Error ECONNRESET: Connection reset by peer URL: https://knowyourerror.com/node/econnreset/ Severity: ERROR · Retryable: yes · Layer: Network Last verified: 2026-10-13 The remote side abruptly closed the socket, often a keep-alive connection the server or LB already timed out. Retry idempotent requests and keep client idle timeouts below the server's. ### What you see ``` Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:216:20) { errno: -104, code: 'ECONNRESET', syscall: 'read' } ``` ## Node.js Error EMFILE: Too many open files URL: https://knowyourerror.com/node/emfile/ Severity: CRITICAL · Retryable: yes · Layer: Runtime Last verified: 2026-10-09 The process hit its file-descriptor limit (ulimit -n), from unclosed streams, unbounded parallel fs calls or file watchers. Close handles, limit concurrency, or raise the limit. ### What you see ``` Error: EMFILE: too many open files, watch '/app/src' at FSWatcher. (node:internal/fs/watchers:247:19) { errno: -24, syscall: 'watch', code: 'EMFILE', filename: '/app/src' } ``` ## Node.js Error ERR_MODULE_NOT_FOUND: Cannot find module (ESM) URL: https://knowyourerror.com/node/err-module-not-found/ Severity: ERROR · Retryable: no · Layer: Runtime Last verified: 2026-10-14 ESM requires full relative paths with file extensions; there is no index.js or extension guessing. Import "./utils/db.js" (also from TypeScript sources with NodeNext). ### What you see ``` node:internal/modules/esm/resolve:283 throw new ERR_MODULE_NOT_FOUND( ^ Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/app/dist/utils/db' imported from /app/dist/index.js Did you mean to import "./utils/db.js"? ``` ## Node.js Error ERR_UNHANDLED_REJECTION: Unhandled promise rejection URL: https://knowyourerror.com/node/err-unhandled-rejection/ Severity: CRITICAL · Retryable: no · Layer: Runtime Last verified: 2026-10-11 A promise rejected with a non-Error value (or no handler) and Node 15+ crashes the process by default. Await or .catch() every promise; log in process.on('unhandledRejection'). ### What you see ``` node:internal/process/promises:394 triggerUncaughtException(err, true /* fromPromise */); ^ [UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "fetch failed".] { code: 'ERR_UNHANDLED_REJECTION' } ``` ## Next.js Error react-hydration-error: Hydration failed: server/client mismatch URL: https://knowyourerror.com/nextjs/react-hydration-error/ Severity: ERROR · Retryable: no · Layer: Client Last verified: 2026-10-23 The HTML React rendered on the server differs from the first client render, so React discards and re-renders the tree. Move time-, locale- or window-dependent values into useEffect and fix invalid nesting like
inside

. ### What you see ``` Error: Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: - A server/client branch `if (typeof window !== 'undefined')`. - Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. - Date formatting in a user's locale which doesn't match the server. - External changing data without sending a snapshot of it along with the HTML. - Invalid HTML tag nesting. It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. https://react.dev/link/hydration-mismatch + 10/23/2026, 2:14:07 PM - 23/10/2026, 14:14:07 ``` ### Likely causes - **Time or locale dependent output** (High): new Date().toLocaleString() renders in the server's timezone and locale, then differently in the browser. - **Browser-only branches during render** (High): typeof window checks or localStorage reads during render produce different markup on server and client. - **Invalid HTML nesting** (Medium):

inside

, or inside : the browser's parser fixes the DOM, so it no longer matches React's tree. ### Fixes #### Render client-only values after mount (Next.js) The first client render matches the server; the locale-specific value is applied after hydration. ```next.js 'use client'; import { useEffect, useState } from 'react'; export function LastSeen({ at }: { at: string }) { const [text, setText] = useState(at.slice(0, 10)); // stable on server + first client render useEffect(() => setText(new Date(at).toLocaleString()), [at]); return ; } ``` #### Skip SSR for components that cannot match (Next.js) ssr:false renders nothing on the server, so there is nothing to mismatch. In Next 15 it must be called from a Client Component. ```next.js 'use client'; import dynamic from 'next/dynamic'; const Chart = dynamic(() => import('./Chart'), { ssr: false, loading: () =>

, }); ``` ### FAQ **Can browser extensions cause hydration errors?** Yes. Extensions like Grammarly or password managers inject attributes into or inputs before React hydrates. Reproduce in a clean profile first. **Is suppressHydrationWarning a real fix?** Only for one element's text or attributes, one level deep. It hides the warning but the content still differs. **Do hydration errors break production?** React recovers by client-rendering, but you lose SSR performance for that subtree and may see layout flicker. ### Prevention - Use suppressHydrationWarning only for single unavoidable text nodes (e.g. timestamps). - Test in an incognito window to rule out extensions. - Run the HTML validator on SSR output for nesting bugs. ## Next.js Error DYNAMIC_SERVER_USAGE: Dynamic server usage URL: https://knowyourerror.com/nextjs/dynamic-server-error/ Severity: ERROR · Retryable: no · HTTP 500 · Layer: Runtime Last verified: 2026-10-21 During next build a route marked static (or force-static) called a dynamic API — cookies(), headers(), searchParams or no-store fetch. Mark the route dynamic, or stop swallowing the internal error in try/catch. ### What you see ``` Error: Dynamic server usage: Route /dashboard couldn't be rendered statically because it used `cookies`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error at u (/app/.next/server/chunks/731.js:1:39518) at Object.get (/app/.next/server/chunks/731.js:1:37862) { description: "Route /dashboard couldn't be rendered statically because it used `cookies`.", digest: 'DYNAMIC_SERVER_USAGE' } ``` ### Likely causes - **try/catch swallows the bailout** (High): Next.js signals dynamic usage by throwing; a try/catch around cookies() or fetch catches it, so the build cannot switch the route to dynamic. - **Forced static rendering** (Medium): export const dynamic = 'force-static' or 'error', or generateStaticParams, conflicts with reading request data. - **Uncached fetch in a static route** (Medium): fetch(url, { cache: 'no-store' }) or revalidate: 0 inside a page expected to prerender. ### Fixes #### Rethrow Next.js internal errors from try/catch (Next.js) unstable_rethrow re-throws errors Next.js uses for control flow, so the route is correctly marked dynamic. ```next.js import { cookies } from 'next/headers'; import { unstable_rethrow } from 'next/navigation'; export default async function Page() { try { const session = (await cookies()).get('session')?.value; return ; } catch (e) { unstable_rethrow(e); // lets DYNAMIC_SERVER_USAGE, redirect(), notFound() propagate return

Something went wrong

; } } ``` #### Declare the route dynamic explicitly (Next.js) Per-request data cannot be prerendered; force-dynamic renders on every request. ```next.js import { headers } from 'next/headers'; export const dynamic = 'force-dynamic'; export default async function Page() { const country = (await headers()).get('x-vercel-ip-country') ?? 'US'; return

Prices for {country}

; } ``` ### FAQ **Why does it only fail in next build, not next dev?** Dev renders every request dynamically. Static prerendering, and therefore this error, only happens at build time. **Is this error harmful in logs at runtime?** If you see it at runtime, an error boundary or try/catch swallowed it. It is a build-time signal, not a user-facing error. **Do I need force-dynamic if I call cookies()?** No. Calling cookies() opts the route into dynamic rendering automatically, unless you forced static or caught the error. ### Prevention - Keep cookies()/headers() calls outside try/catch. - Check the build output: ƒ = dynamic, ○ = static. - Move request-specific UI into a small dynamic component. ## Next.js Error next-image-unconfigured-host: next/image hostname not configured URL: https://knowyourerror.com/nextjs/next-image-unconfigured-host/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: Runtime Last verified: 2026-10-19 next/image only optimizes remote images from hosts allowed in next.config. Add the host to images.remotePatterns (protocol, hostname, optional pathname) and restart the dev server. ### What you see ``` Error: Invalid src prop (https://images.unsplash.com/photo-1501785888041-af3ef285b470?w=1200) on `next/image`, hostname "images.unsplash.com" is not configured under images in your `next.config.js` See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host ``` ### Likely causes - **Remote host not allowlisted** (High): Any absolute src URL must match an entry in images.remotePatterns, to stop the optimizer being used as an open proxy. - **Pattern too narrow** (Medium): hostname matches exactly; cdn.example.com does not match example.com, and pathname /img/** does not match /images/. - **Config change without restart** (Low): next.config.js is read at startup, so edits need the dev server restarted. ### Fixes #### Add remotePatterns for the image host (Next.js) remotePatterns supports wildcards (** for subdomains/paths) and replaces the deprecated images.domains. ```next.js // next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { images: { remotePatterns: [ { protocol: 'https', hostname: 'images.unsplash.com', pathname: '/**' }, { protocol: 'https', hostname: '**.cloudfront.net' }, ], }, }; export default nextConfig; ``` #### Opt out of optimization for user-supplied URLs (Next.js) unoptimized skips the image optimizer, so arbitrary hosts are allowed. Use it when you cannot enumerate hosts. ```next.js {user.name} ``` ### FAQ **Is images.domains still supported?** It is deprecated since Next.js 14 in favor of remotePatterns, which also restricts protocol and path. **Why does it work in dev but fail on Vercel?** Usually a different image host in production env vars. The error message shows the exact hostname to add. **Can I allow every host?** hostname: '**' works but turns your optimizer into an open image proxy you pay for. Avoid it in production. ### Prevention - Keep patterns as narrow as practical. - Restart next dev after editing next.config. - Proxy user uploads through your own CDN host. ## Next.js Error missing-suspense-with-csr-bailout: useSearchParams() should be wrapped in a suspense boundary URL: https://knowyourerror.com/nextjs/missing-suspense-with-csr-bailout/ Severity: ERROR · Retryable: no · Layer: Runtime Last verified: 2026-10-17 A statically prerendered page uses useSearchParams() without a boundary, which would client-render the whole page. Wrap the component that reads search params in . ### What you see ``` ⨯ useSearchParams() should be wrapped in a suspense boundary at page "/search". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout Error occurred prerendering page "/search". ``` ## Next.js Error module-not-found: Module not found: Can't resolve URL: https://knowyourerror.com/nextjs/module-not-found/ Severity: ERROR · Retryable: no · Layer: Runtime Last verified: 2026-10-15 A Node-only module (fs, path, crypto) was imported into code bundled for the browser, or a path alias/case is wrong. Keep server code in Server Components or add import 'server-only'. ### What you see ``` ./src/lib/config.ts:1:1 Module not found: Can't resolve 'fs' > 1 | import fs from 'fs'; | ^ https://nextjs.org/docs/messages/module-not-found Import trace for requested module: ./src/components/Settings.tsx ``` ## Next.js Error next-router-not-mounted: NextRouter was not mounted URL: https://knowyourerror.com/nextjs/next-router-not-mounted/ Severity: ERROR · Retryable: no · Layer: Client Last verified: 2026-10-12 useRouter was imported from next/router inside the App Router. Import useRouter, usePathname and useSearchParams from next/navigation instead. ### What you see ``` Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted ``` ## Next.js Error sync-dynamic-apis: params should be awaited URL: https://knowyourerror.com/nextjs/sync-dynamic-apis/ Severity: WARNING · Retryable: no · Layer: Runtime Last verified: 2026-10-22 In Next.js 15, params, searchParams, cookies() and headers() are async. Type params as a Promise and await it; run npx @next/codemod@canary next-async-request-api . to migrate. ### What you see ``` Error: Route "/blog/[slug]" used `params.slug`. `params` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis ``` ## Anthropic API Error overloaded_error: API temporarily overloaded (529) URL: https://knowyourerror.com/anthropic/529-overloaded-error/ Severity: WARNING · Retryable: yes · HTTP 529 · Layer: AI Provider Last verified: 2026-10-22 Anthropic’s API is briefly out of capacity across all users; your request and your limits are fine. Retry with exponential backoff, and fall back to another model if it persists. ### What you see ``` anthropic.InternalServerError: Error code: 529 - {"type": "error", "error": {"type": "overloaded_error", "message": "Overloaded"}, "request_id": "req_011CUQ5bJ7xVwKp3oRz9TnYd"} ``` ### Likely causes - **Provider-wide capacity spike** (High): Demand for a model exceeds available capacity for a few seconds or minutes. It is not tied to your organization’s usage. - **Retries disabled or too few** (Medium): Clients created with maxRetries: 0, or wrapped in a job queue that fails fast, surface a transient 529 as a hard error. - **Synchronous fan-out at peak times** (Low): Batch jobs that fire hundreds of parallel calls during peak hours hit overloads more often than the same work spread out. ### Fixes #### Let the SDK retry with backoff (TypeScript) The official SDKs already retry with exponential backoff (2 retries by default). Raising the count rides out short overloads without custom code. ```typescript const client = new Anthropic({ maxRetries: 5 }); // backoff on 408, 409, 429 and 5xx incl. 529 const msg = await client.messages.create(req); ``` #### Fall back to a second model (Python) Capacity is per model. A smaller model is usually available when a larger one is overloaded; log the fallback so you can see how often it happens. ```python import anthropic try: msg = client.messages.create(model="claude-sonnet-5-5", **req) except anthropic.APIStatusError as e: if e.status_code != 529: raise msg = client.messages.create(model="claude-haiku-5-5", **req) ``` ### FAQ **Does a 529 count against my rate limit?** It signals provider capacity, not your quota. Your rate-limit headers tell you how much of your own budget remains. **Is 529 a real HTTP status?** It is not in the HTTP standard. Anthropic uses it to distinguish overload from 500 api_error; SDKs treat it as a retryable server error. **How long do overloads last?** Usually seconds to a few minutes. Exponential backoff with jitter up to about a minute covers most of them. ### Prevention - Treat 529 like 503: retry, never alert on a single occurrence. - Use the Message Batches API for work that does not need an immediate answer. - Alert on the 529 rate over 5 minutes, not individual errors. ## Anthropic API Error rate_limit_error: Rate limit exceeded (429) URL: https://knowyourerror.com/anthropic/429-rate-limit-error/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: AI Provider Last verified: 2026-10-22 Your organization exceeded its requests-per-minute or tokens-per-minute limit for this model. Wait for the retry-after header, then cut token usage with prompt caching or smaller prompts. ### What you see ``` anthropic.RateLimitError: Error code: 429 - {"type": "error", "error": {"type": "rate_limit_error", "message": "This request would exceed your organization's rate limit of 30,000 input tokens per minute."}, "request_id": "req_011CUQ7x2kGd8sWb1mHf4ZqA"} retry-after: 12 ``` ### Likely causes - **Input tokens per minute (ITPM)** (High): Large prompts (long documents, big tool definitions) use up the per-minute input token budget long before the request count limit. - **Bursty concurrency** (Medium): A batch job sends many requests in the same second; limits are enforced continuously, so short bursts trip them even when the minute average is fine. - **Low usage tier** (Medium): New organizations start on lower limits that rise with spend. ### Fixes #### Honour retry-after (TypeScript) retry-after says exactly when capacity frees up. Waiting less just produces another 429. ```typescript import Anthropic from "@anthropic-ai/sdk"; for (const doc of docs) { try { await client.messages.create(build(doc)); } catch (e) { if (!(e instanceof Anthropic.RateLimitError)) throw e; const wait = Number(e.headers?.["retry-after"] ?? 10); await new Promise(r => setTimeout(r, wait * 1000)); } } ``` #### Cache the repeated prefix (Python) On current models, cached input tokens read from the prompt cache do not count toward the input-tokens-per-minute limit, and they cost less. ```python client.messages.create(model=MODEL, max_tokens=1024, system=[{"type": "text", "text": LONG_SYSTEM_PROMPT, "cache_control": {"type": "ephemeral"}}], messages=msgs) ``` ### FAQ **Which limit did I hit?** The message names it (requests, input tokens or output tokens per minute), and the anthropic-ratelimit-* headers show what remains for each. **Why 429 when the dashboard shows low usage?** Limits are enforced over short windows, so a one-second burst can exceed them even if the minute total looks small. ### Prevention - Read the anthropic-ratelimit-* response headers and slow down before you hit zero. - Cap concurrency per model instead of firing all requests at once. - Move non-urgent work to the Message Batches API. ## Anthropic API Error invalid_request_error: Prompt is too long URL: https://knowyourerror.com/anthropic/prompt-is-too-long/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: AI Provider Last verified: 2026-10-22 The prompt plus requested output exceeds the model’s context window. Trim history, summarize old turns or retrieve only the relevant chunks; retrying the same request always fails. ### What you see ``` anthropic.BadRequestError: Error code: 400 - {"type": "error", "error": {"type": "invalid_request_error", "message": "prompt is too long: 215337 tokens > 200000 maximum"}} ``` ## Anthropic API Error authentication_error: Invalid x-api-key (401) URL: https://knowyourerror.com/anthropic/401-authentication-error/ Severity: CRITICAL · Retryable: no · HTTP 401 · Layer: AI Provider Last verified: 2026-10-22 The API key is missing, revoked or sent in the wrong header. Anthropic expects it in x-api-key, not Authorization: Bearer. ### What you see ``` {"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"}} ``` ## OpenAI API Error insufficient_quota: You exceeded your current quota URL: https://knowyourerror.com/openai/429-insufficient-quota/ Severity: CRITICAL · Retryable: no · HTTP 429 · Layer: AI Provider Last verified: 2026-10-22 Same 429 status as a rate limit, but the cause is billing: the project has no credit left or hit its monthly budget. Retrying never helps; add credits or raise the budget. ### What you see ``` openai.RateLimitError: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}} ``` ### Likely causes - **Prepaid credits used up** (High): Pay-as-you-go accounts stop serving requests when the credit balance reaches zero, unless auto-recharge is on. - **Project or org budget reached** (Medium): A monthly budget set on the project or organization blocks further usage until it is raised or the month resets. - **Key belongs to a different project** (Medium): The key comes from a project with no billing attached, while the dashboard you checked shows another project. ### Fixes #### Don’t retry quota errors (TypeScript) Both errors arrive as RateLimitError with status 429. The code field is the only reliable way to tell a billing stop from a temporary limit. ```typescript import OpenAI from "openai"; catch (e) { if (e instanceof OpenAI.RateLimitError && e.code === "insufficient_quota") { alertBilling(e); // page a human; retrying cannot fix billing throw e; } if (e instanceof OpenAI.RateLimitError) return retryWithBackoff(req); throw e; } ``` #### Same split in Python (Python) Fail fast on billing errors so on-call sees one clear alert instead of a retry storm. ```python except openai.RateLimitError as e: if e.code == "insufficient_quota": raise BillingError("OpenAI credits exhausted") from e time.sleep(backoff()); return call() ``` ### FAQ **I just added credits, why does it still fail?** Check that the API key belongs to the project you funded; new keys and balance updates can take a few minutes to apply. **Is ChatGPT Plus the same as API credit?** No. ChatGPT subscriptions and API billing are separate; the API needs its own credit balance. ### Prevention - Turn on auto-recharge for production projects. - Set a budget alert below the hard limit so you hear about it before requests fail. - Alert on code, not status: insufficient_quota is a billing incident, rate_limit_exceeded is a capacity one. ## OpenAI API Error context_length_exceeded: Maximum context length exceeded URL: https://knowyourerror.com/openai/context-length-exceeded/ Severity: ERROR · Retryable: no · HTTP 400 · Layer: AI Provider Last verified: 2026-10-22 The messages (plus tools and the output budget) do not fit in the model’s context window. Count tokens before sending and trim or summarize the oldest turns. ### What you see ``` openai.BadRequestError: Error code: 400 - {'error': {'message': "This model's maximum context length is 128000 tokens. However, your messages resulted in 131072 tokens. Please reduce the length of the messages.", 'type': 'invalid_request_error', 'param': 'messages', 'code': 'context_length_exceeded'}} ``` ### Likely causes - **Chat history grows without a cap** (High): Every turn is resent, so a long conversation eventually exceeds the window. - **Retrieved documents stuffed in whole** (Medium): RAG pipelines paste entire files instead of the top-k relevant chunks. - **Large tool schemas** (Low): Dozens of function definitions count toward input tokens on every request. ### Fixes #### Trim history to a token budget (Python) Leave headroom below the hard limit for the reply; the window covers input and output together. ```python import tiktoken enc = tiktoken.encoding_for_model("gpt-4o") def fit(history, budget=120_000): system, rest = history[0], history[1:] keep, used = [], 0 for m in reversed(rest): # newest first n = len(enc.encode(m["content"])) + 4 if used + n > budget: break keep.append(m); used += n return [system] + keep[::-1] # always keep the system prompt resp = client.chat.completions.create(model="gpt-4o", messages=fit(history)) ``` #### Summarize old turns (TypeScript) Summaries keep long-running context in a fraction of the tokens. ```typescript if (countTokens(messages) > 100_000) { const summary = await summarize(messages.slice(1, -10)); messages = [messages[0], { role: "system", content: `Earlier conversation: ${summary}` }, ...messages.slice(-10)]; } await openai.chat.completions.create({ model, messages }); ``` ### FAQ **Does max_tokens count toward the limit?** Yes. Input tokens plus the requested output must fit within the context window. **Should I just switch to a bigger model?** It helps once, but unbounded history will overflow any window. Add trimming either way. ### Prevention - Log token counts per request so growth shows up before it fails. - Send top-k retrieved chunks, not whole documents. - Only include the tools the current step can use. ## OpenAI API Error rate_limit_exceeded: Rate limit reached (TPM/RPM) URL: https://knowyourerror.com/openai/429-rate-limit-exceeded/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: AI Provider Last verified: 2026-10-22 You exceeded requests or tokens per minute for this model. Retry after the suggested delay with backoff; read the x-ratelimit-* headers to pace requests. ### What you see ``` {"error":{"message":"Rate limit reached for gpt-4o in organization org-XXXX on tokens per min (TPM): Limit 30000, Used 27410, Requested 4120. Please try again in 3.06s.","type":"tokens","param":null,"code":"rate_limit_exceeded"}} ``` ## OpenAI API Error invalid_api_key: Incorrect API key provided URL: https://knowyourerror.com/openai/401-invalid-api-key/ Severity: CRITICAL · Retryable: no · HTTP 401 · Layer: AI Provider Last verified: 2026-10-22 The key is wrong, revoked or has stray whitespace. Generate a new key in the right project and check the env var actually loads at runtime. ### What you see ``` {"error":{"message":"Incorrect API key provided: sk-proj-****Xa9Q. You can find your API key at https://platform.openai.com/account/api-keys.","type":"invalid_request_error","param":null,"code":"invalid_api_key"}} ``` ## OpenAI API Error model_not_found: The model does not exist or you do not have access URL: https://knowyourerror.com/openai/404-model-not-found/ Severity: ERROR · Retryable: no · HTTP 404 · Layer: AI Provider Last verified: 2026-10-22 Typo in the model ID, a retired snapshot, or a model your project is not allowed to use. List available models with GET /v1/models using the same key. ### What you see ``` {"error":{"message":"The model `gpt-4o-mini-2099` does not exist or you do not have access to it.","type":"invalid_request_error","param":null,"code":"model_not_found"}} ``` ## Google Gemini API Error RESOURCE_EXHAUSTED: Quota exceeded (429) URL: https://knowyourerror.com/gemini/429-resource-exhausted/ Severity: WARNING · Retryable: yes · HTTP 429 · Layer: AI Provider Last verified: 2026-10-22 You hit a Gemini API limit: requests per minute, tokens per minute or requests per day for this model and tier. Back off and retry for per-minute limits; per-day limits need billing or a higher tier. ### What you see ``` { "error": { "code": 429, "message": "You exceeded your current quota, please check your plan and billing details.", "status": "RESOURCE_EXHAUSTED" } } ``` ### Likely causes - **Free-tier limits** (High): Free tier limits per model are low and include a daily request cap that resets at midnight Pacific time. - **Bursts above requests per minute** (Medium): Parallel calls from a batch job exceed the per-minute limit even when the daily total is small. - **Shared project quota** (Low): Several apps use keys from the same Google Cloud project and share its quota. ### Fixes #### Back off on 429 (TypeScript) Per-minute limits clear within a minute; jittered backoff spreads retries so they don’t collide again. ```typescript import { GoogleGenAI } from "@google/genai"; const ai = new GoogleGenAI({}); async function generate(contents, tries = 5) { for (let i = 0; ; i++) { try { return await ai.models.generateContent({ model: "gemini-2.5-flash", contents }); } catch (e) { if (e.status !== 429 || i === tries - 1) throw e; await new Promise(r => setTimeout(r, Math.random() * 1000 * 2 ** i)); } } } ``` #### Cap concurrency in batch jobs (Python) Five in flight keeps a batch under the per-minute limit instead of failing most of it at once. ```python sem = asyncio.Semaphore(5) async def limited(d): async with sem: return await summarize(d) await asyncio.gather(*(limited(d) for d in docs)) ``` ### FAQ **Is RESOURCE_EXHAUSTED always my quota?** Usually. Occasionally it reflects shared capacity on a model; in both cases backoff is the right first response. **When does the daily limit reset?** Requests-per-day quotas reset at midnight Pacific time. ### Prevention - Check your tier’s limits per model in Google AI Studio. - Enable billing on the project for production traffic. - Use the Batch API for large offline jobs. ## Google Gemini API Error API_KEY_INVALID: API key not valid URL: https://knowyourerror.com/gemini/400-api-key-invalid/ Severity: CRITICAL · Retryable: no · HTTP 400 · Layer: AI Provider Last verified: 2026-10-22 Gemini reports a bad key as 400 INVALID_ARGUMENT with reason API_KEY_INVALID, not 401. Create a key in Google AI Studio and check it is restricted to the Generative Language API. ### What you see ``` { "error": { "code": 400, "message": "API key not valid. Please pass a valid API key.", "status": "INVALID_ARGUMENT", "details": [{ "@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "API_KEY_INVALID", "domain": "googleapis.com" }] } } ``` ## Google Gemini API Error SAFETY: Response blocked: finishReason SAFETY URL: https://knowyourerror.com/gemini/finish-reason-safety/ Severity: INFO · Retryable: no · Layer: AI Provider Last verified: 2026-10-22 HTTP 200, but the candidate has no text because a safety filter stopped it. Check finishReason before reading the text, and adjust the prompt or safety settings for legitimate use cases. ### What you see ``` "candidates": [{ "finishReason": "SAFETY", "safetyRatings": [{ "category": "HARM_CATEGORY_DANGEROUS_CONTENT", "probability": "MEDIUM", "blocked": true }] }] ``` ## Google Gemini API Error DEADLINE_EXCEEDED: Deadline exceeded (504) URL: https://knowyourerror.com/gemini/504-deadline-exceeded/ Severity: WARNING · Retryable: yes · HTTP 504 · Layer: AI Provider Last verified: 2026-10-22 The model did not finish within the deadline, usually on very large prompts or long outputs. Stream the response, raise the client timeout, or split the work. ### What you see ``` {"error":{"code":504,"message":"Deadline expired before operation could complete.","status":"DEADLINE_EXCEEDED"}} ``` ## Guides - [Three 429s, three meanings: OpenAI, Anthropic and Gemini](https://knowyourerror.com/guides/three-429s-ai-providers/): The same status code means “slow down” at one provider and “pay your bill” at another. Here is how to tell them apart in one catch block. - [P2024 on serverless: the connection-pool math nobody does](https://knowyourerror.com/guides/prisma-p2024-serverless-connection-pool-math/): Every cold start opens its own pool. Here is how 40 Lambdas turn into 200 Postgres connections, and the three settings that bring it back to 40. - [Retry or fix? A decision tree for every error in your logs](https://knowyourerror.com/guides/retry-or-fix-decision-tree/): Half of all retry loops make incidents worse. Four questions decide whether to back off and try again, or stop and change the request. - [Reading revert data like a stack trace](https://knowyourerror.com/guides/decode-evm-revert-data/): 0xe450d38c followed by 192 hex characters is not noise. It is a function call in reverse, and you can decode it in one line. - [Cloudflare 52x errors, explained by where the request died](https://knowyourerror.com/guides/cloudflare-52x-where-the-request-died/): 520, 522, 524 and 525 each name a different hop between the edge and your origin. Draw the path once and the fix is obvious. - [401 or 403? Ask one question](https://knowyourerror.com/guides/401-vs-403-one-question/): “Do I know who you are?” If no, 401. If yes but you can’t do this, 403. Everything else is detail. - [Make 550 5.7.1 go away: SPF, DKIM and DMARC in 20 minutes](https://knowyourerror.com/guides/smtp-550-spf-dkim-dmarc-in-20-minutes/): Since 2024 Gmail and Yahoo reject unauthenticated bulk mail outright. Three DNS records fix most of it. - [Next.js hydration errors: the five causes, ranked](https://knowyourerror.com/guides/nextjs-hydration-errors-five-causes/): Dates, window checks, invalid HTML nesting, random IDs and browser extensions. In that order. - [ERR_REQUIRE_ESM in 2026: you might just need to upgrade Node](https://knowyourerror.com/guides/err-require-esm-in-2026/): require() of ES modules works by default since Node 22.12 and 20.19. When it still fails, it is one of two edge cases. - [Postgres deadlocks are retryable. Lock ordering makes them rare.](https://knowyourerror.com/guides/postgres-deadlocks-are-retryable/): 40P01 is Postgres doing its job: it picked a victim so the other transaction could finish. Retry the victim, then fix the order.