CVE-2026-58041 is a medium-severity Node.js vulnerability in the built-in node:sqlite SQLTagStore API. On affected Node 24 and 26 releases, reusing a stale iterator could re-execute a write statement rather than simply continue reading an old result.

Unexpected write replay is serious when a statement changes balances, inventory, job state, counters, or audit records. Patch to Node.js 24.18.1 or 26.5.1 and review any SQLTagStore iterator usage.

Quick answer

Upgrade the runtime, never retain or reuse an iterator after its intended operation, and design write operations to be idempotent where business correctness requires it. Add a test that proves a consumed or stale iterator cannot repeat a mutation.

import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync(":memory:");
db.exec(`
  CREATE TABLE jobs (
    id TEXT PRIMARY KEY,
    attempts INTEGER NOT NULL
  );
  INSERT INTO jobs VALUES ('job-1', 0);
`);

// Keep statement execution local to one operation.
function incrementAttempts(jobId) {
  db.prepare(`
    UPDATE jobs
    SET attempts = attempts + 1
    WHERE id = ?
  `).run(jobId);
}

This example uses a prepared statement directly. It avoids sharing iterator state across requests or jobs.

Keep each statement result inside the request or job that created it. Do not place an iterator in a module-level cache, return it across an asynchronous ownership boundary, or let two handlers consume it.

What can go wrong

Iterators normally feel read-oriented: obtain a sequence and consume rows. SQLTagStore combines tagged templates, cached statements, and iteration. The vulnerability could let stale iterator state trigger another execution of a write.

The business outcome depends on the SQL:

  • an increment can happen twice;
  • an insert may duplicate data or fail on a unique key;
  • a state transition may skip expected validation;
  • an audit event may appear more than once;
  • a debit-like operation may be repeated.

Database constraints may contain some outcomes, but they do not make unintended re-execution acceptable.

Find SQLTagStore use

Search the codebase and dependencies for the API and for iterators held beyond a local scope.

rg -n "SQLTagStore|createTagStore|\\.iterate\\(|Symbol\\.iterator|Symbol\\.asyncIterator" .

Inspect class fields, caches, closures, generators, and request-scoped objects. A local for...of loop that is created and exhausted immediately is easier to reason about than an iterator saved for later.

Check the runtime version inside every service using built-in SQLite. Node 22 is not listed for this issue because the affected API exposure differs.

Test for repeated side effects

Use a disposable database and a write with an observable count. Execute the intended operation once, consume or retain the iterator in the same pattern as production, then attempt the stale operation. The final count must still represent one business action.

Avoid reproducing unsafe behavior in a real database. The test should run against patched Node and remain as a regression guard for future runtime upgrades.

Also test retries at the application layer. A patched runtime does not protect a payment or job mutation from duplicate queue delivery, network retries, or client resubmission.

Add idempotency where it belongs

For externally triggered writes, store an idempotency key or event identifier with a unique constraint. Apply the mutation and record the key in one transaction.

CREATE TABLE processed_events (
  event_id TEXT PRIMARY KEY,
  processed_at TEXT NOT NULL
);

The handler first claims the event ID, then changes business state in the same transaction. A repeated delivery becomes a known no-op.

Idempotency is defense in depth here. It protects against many replay sources, while upgrading Node corrects the specific iterator flaw.

Patch and observe production

Move Node 24 workloads to 24.18.1 and Node 26 workloads to 26.5.1. Rebuild immutable images and run database tests before canary deployment.

Monitor duplicate-key errors, unexpected row counts, transaction rollbacks, job-attempt counters, and business reconciliation signals. If the affected code processed sensitive writes before patching, inspect domain records rather than relying only on application exceptions.

Primary sources

The runtime update removes the known stale-iterator path. Local iterator ownership, database constraints, transactional idempotency, and reconciliation provide the broader protection production write systems still need.

After rollout, reconcile one sensitive write flow against its source events.