Skip to content

Revision 1 — the current text. The evidence on this page is this revision’s own — it has not been carried forward from, or to, any other revision.

Fixing D1's "too many API requests by single worker invocation" error

Covers the D1-specific per-invocation request cap being hit by an N+1 query pattern or by combining many D1 calls with other subrequests in one invocation. Does not cover the general Workers subrequest limit for fetch()-only Workers with no D1 involved (see Workers platform limits), and does not cover D1's separate CPU-time or memory-limit resets, which return different error text entirely.

Unverifiedno reproductions yetWhy this confidence?
Revision 1published by DevYou curation

Symptoms

The diagnostic path

8 steps, exactly as this revision published them.

  1. Step 1 · StartD1_ERROR: too many API requests by single worker invocation

    A single Worker invocation that makes many separate calls to D1 fails partway through with this D1-specific error, distinct from D1's CPU-time or memory-limit reset messages.

    What happens next

    • passed step 2, Count how many separate D1 calls happen in this one invocation
  2. Step 2 · TestCount how many separate D1 calls happen in this one invocation

    Look for an N+1 pattern: a loop that runs one env.DB.prepare(...).run() (or .all()/.first()) per row instead of one query covering all the rows, or one db.batch() call.

    Read-onlysh
    grep -n "env.DB.prepare\|\.first(\|\.all(\|\.run(" src/**/*.ts

    Expected result

    A `.prepare(...)` call sitting inside a `for` loop or `.map()`/`.forEach()` over a list of rows.

    What happens next

    • passed step 3, Replace the N+1 loop with one query or one batch
    • failed step 4, Check whether other subrequests share the invocation
    • unknown step 3, Replace the N+1 loop with one query or one batch
  3. Step 3 · FixReplace the N+1 loop with one query or one batch

    Collapse the per-row queries into a single query with WHERE id IN (...), or, when each row needs a different statement (e.g. different values on an UPDATE), combine them into one db.batch([...]) call. Either way this turns N D1 requests into one.

    Changes statets

    Changes system or service state. Review before running.

    Changes how the data is fetched (one query instead of N), not what is returned. For writes, batching via db.batch() still executes every statement — it just sends them in one round trip.

    // Before: one query per row
    // for (const id of ids) { await env.DB.prepare("SELECT * FROM items WHERE id = ?").bind(id).first(); }
    
    // After: one query for all rows
    const placeholders = ids.map(() => "?").join(",");
    const { results } = await env.DB.prepare(
      `SELECT * FROM items WHERE id IN (${placeholders})`
    ).bind(...ids).all();

    What happens next

    • passed step 5, Re-run with representative data volume
  4. Step 4 · TestCheck whether other subrequests share the invocation

    D1 calls count against the same per-invocation subrequest budget as fetch(), KV, and R2 calls. If this invocation also makes several other bound-service calls alongside its D1 queries, the combined total — not D1 alone — may be what's tipping it over.

    Read-onlysh
    grep -n "env\.\(DB\|KV\|R2\|BUCKET\)\.\|fetch(" src/**/*.ts

    Expected result

    Several distinct bound-service calls (D1, KV, R2, fetch) all inside the same handler.

    What happens next

    • passed step 6, Reduce total subrequests, or raise the configured limit
    • failed step 7, Not a request-count issue — escalate
    • unknown step 7, Not a request-count issue — escalate
  5. Step 5 · Verify the fixRe-run with representative data volume

    Repeat the request that previously failed, using the same (or larger) row count, and confirm it completes without the D1_ERROR.

    Read-onlysh
    curl -s https://your-worker.example.workers.dev/bulk-endpoint

    Expected result

    A normal 200 response covering the full data set, with no D1_ERROR in wrangler tail.

    What happens next

    • passed step 8, Resolved
    • failed step 7, Not a request-count issue — escalate
    • unknown step 7, Not a request-count issue — escalate
  6. Step 6 · FixReduce total subrequests, or raise the configured limit

    First prefer reducing the total number of calls (the N+1 fix above, or caching repeated lookups). If the workload genuinely needs a high subrequest count, Workers Paid plans can raise the configured limits.subrequests above the 10,000 default, up to 10,000,000.

    Changes statejsonc

    Changes system or service state. Review before running.

    Raises the maximum number of subrequests this Worker may make per invocation. It does not reduce or change any query results — it only allows more calls before the runtime cuts the invocation off. Available on the Workers Paid plan.

    {
      "limits": {
        "subrequests": 50000
      }
    }

    What happens next

    • passed step 5, Re-run with representative data volume
  7. Step 7 · EndNot a request-count issue — escalate

    If the invocation genuinely makes few D1 calls and this error still appears, check for an actual infinite loop or unbounded recursion calling the same query repeatedly, rather than a data-volume problem.

  8. Step 8 · EndResolved

    The invocation now makes far fewer D1 requests (or has room for the ones it needs), and the error no longer occurs.

Sources

Why this confidence?

What would strengthen it: 6 more independent reproductions. Reproductions from 3 more distinct environments.

This counts only what was recorded against revision 1 itself. Nothing reported against another revision is included here — see the revision history for why.