# What changed in pg-boss

> 51 changes in pg-boss 12 from its official release notes, upgrade guides and advisories: 15 breaking, 2 deprecations, 12 new APIs and options. Each one cited. Sources checked 2026-10-21. HTML: https://unstale.sh/npm/pg-boss/

To see which of these apply to a repository, run `npx @unstale/cli init` in it.

## pg-boss 12

### Breaking changes

- Deletes in batches and resolves to the number of jobs deleted; failures can leave a partial delete. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Deletes in batches and resolves to the number of jobs deleted; failures can leave a partial delete. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns matching jobs oldest first and at most 1,000 by default; callers can use limit and after to read further pages. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Reads at most limit jobs of each kind, defaulting to 1,000 and allowing up to 100,000; total is no longer exact and counts only jobs read. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- After upgrading, totalCount retains its prior count until the queue's first maintenance pass, then may temporarily be off after concurrent deletes or resumes during counting. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- A job naming a missing dead-letter queue now rejects with Dead letter queue <name> does not exist. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Deleting a partitioned queue no longer waits indefinitely on concurrent sends; it retries lock acquisition for about 3 seconds, then rejects and leaves the queue unchanged. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Flow creation now rejects when a queue was deleted by another instance. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Inserting into a queue deleted by another instance now rejects with Queue <name> does not exist instead of reporting nothing done. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Previously stored schedules with empty expressions are skipped by the cron pass with an invalid_schedule warning instead of firing every minute. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- schedule() rejects empty, blank, or non-string cron expressions; previously an empty expression could be read as every minute. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- schedule() writes on a passed db, and no longer stores db with the schedule. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Sending to a queue deleted by another instance now rejects with Queue <name> does not exist instead of resolving null; in a passed transaction, COMMIT can fail with 23503. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- stop() settles in-flight claims before completing; a claim not ready within the timeout is failed without starting its handler, so shutdown can exceed timeout. With graceful: false the job is failed at once. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Upserting into a queue deleted by another instance now rejects with Queue <name> does not exist instead of resolving null or reporting nothing done. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)

### Deprecations

- The singletonsActive field on getQueue() and getQueues() is deprecated and will be removed in the next major. (12.37.1) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.1)
- backend: 'yugabytedb' still works but emits a Node DeprecationWarning with code PGBOSS_DEP_YUGABYTEDB and will be rejected in the next major. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)

### New APIs and options

- Returns at most 1,000 rows by default in version and creation order; supports limit and after paging. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns at most 1,000 rows by default in key order; supports limit and after paging. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns at most 1,000 rows by default, ordered by queue name and id; supports limit and after paging. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns at most 1,000 rows by default, ordered by queue name and id; supports limit and after paging. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns at most 1,000 rows by default, oldest first; supports limit and after paging. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns at most 1,000 rows by default in name order; supports limit and after paging through its options argument. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns at most 1,000 rows by default, ordered by queue name and key; supports limit and after paging. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- An options type for bounded list reads is now exported. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- The options type for previewRedrive is now exported. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- getUninstallPlans(schema, { backend }) returns SQL to remove pg-boss objects from a shared schema without CASCADE. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- The uninstall command prints SQL to remove pg-boss objects from a shared schema without CASCADE. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Pass attempts to retry start after database failures, waiting 1 second between tries and doubling up to 30 seconds; configuration AssertionErrors are not retried. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)

### Performance

- Expired jobs and orphaned dependencies are deleted a queue at a time, with each batch in its own transaction, limiting long-held locks and xmin horizons. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)

### Behavior and default changes

- Once a pending background command is found, pending commands run back to back; commands from one migration on one table run in order and wait for predecessors to complete. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Deletes in batches and resolves to the number of jobs deleted, or null when TRUNCATE is used; failures can leave a partial delete. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- On a shared table, deletes jobs in batches before deleting the queue; a failure can leave jobs partially deleted and the queue in place. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Returns one row per status, ordered by status, without a limit. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- When enabled, handler_overrun, a failed LISTEN/NOTIFY listener, and a disabled xmin_horizon check are written to the warning table. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Runs pending background migration commands after the migration commits; on failure, exits with an error listing statements left to run by hand. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Without table partitioning, old snapshots are deleted in batches on every supervise pass; with partitioning, whole days are still dropped. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- The timeout now bounds how long stop waits for a running background migration command; graceful: false ends the command at once. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Expired jobs are failed in batches, so recovery after a fleet outage uses short transactions. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- If a handler outlives its timeout, its worker waits for it to return before fetching again and emits a handler_overrun warning; a handler that never returns holds the worker until stopped. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- unschedule() now runs its write on a passed db. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)

### Fixes and other changes

- A custom adapter's executeSql must return an array with one result per statement for multi-statement queries; returning only the last result breaks maintenance passes and background migration claims. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Expiry no longer deletes jobs from another queue when jobs share an id on the shared job table. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- The old index now remains in place until its replacement has been built, preventing slow fetches after a failed background index build. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Multi-statement query results over postgres-js are flattened, allowing pg-boss to find rows from statements before the last. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Redriving from a dead letter queue on the shared job table now touches only that queue's jobs, avoiding deletion or in-place failure of another queue's job with the same id. (12.38.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.38.0)
- Fetches from singleton and stately queues now check whether a key has an active job instead of relying on the cached list, preventing completed-job keys from staying blocked. (12.37.1) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.1)
- A pass that loses the lock now lets the next pass count its queues, keeping counts within about one interval for getQueue(), getQueueStats(), the dashboard, queue_stats history, and large-queue warnings. (12.37.1) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.1)
- Using pg-boss before start() now reports that start() must be called, rather than naming the internal open(). (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- publish() skips a subscribed queue deleted while it is running, unless the send ran on a passed db. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
- Concurrent upserts with the same singletonKey now agree on one job instead of inserting duplicates; on CockroachDB, a later upsert may fail with 40001 and is retried up to 3 times when pg-boss owns the transaction. (12.37.0) — [release notes](https://github.com/timgit/pg-boss/releases/tag/12.37.0)
