---
title: "Release 3.1 | SurrealDB"
description: "SurrealDB 3.1.6 is a reliability rollup on the 3.1 line, backporting fixes that have already landed on the 3.2 and 3.3 lines. It fixes a batch of concurrent…"
url: https://surrealdb.com/releases/3.1
---

# SurrealDB 3.1

[Latest patch: 3.1.6](https://surrealdb.com/releases/3.1#release-3-1-6) [All releases](https://surrealdb.com/releases)

**Patches.** 10 patch releases on the 3.1 line.

**Latest.** 3.1.6, released Sep 1, 2026.

**Stable.** Ready for production, with every patch's notes below.

1. ![Babcock](https://surrealdb.com/assets/static/babcock.lo4rnVg1.svg)
2. ![ING](https://surrealdb.com/assets/static/ing.X3I6S3_V.svg)
3. ![British Airways](https://surrealdb.com/assets/static/british-airways.KEsZiwV-.svg)
4. ![Nvidia](https://surrealdb.com/assets/static/nvidia.DaIEuMil.svg)
5. ![Apple](https://surrealdb.com/assets/static/apple.D5pq4flY.svg)
6. ![SpaceX](https://surrealdb.com/assets/static/spacex.CQJEk-IL.svg)
7. ![Samsung](https://surrealdb.com/assets/static/samsung.CH-vQgnb.svg)
8. ![adidas](https://surrealdb.com/assets/static/adidas.DdTC5qhk.svg)
9. ![Tencent](https://surrealdb.com/assets/static/tencent.paQmLxyy.svg)
10. ![Alibaba](https://surrealdb.com/assets/static/alibaba.B16idgfM.svg)
11. ![PolyAI](https://surrealdb.com/assets/static/poly-ai.c3w_fAg6.svg)
12. ![Later](https://surrealdb.com/assets/static/later.Ds736jFO.svg)
13. ![Verizon](https://surrealdb.com/assets/static/verizon.BI7CajdX.svg)
14. ![Liberty Mutual](https://surrealdb.com/assets/static/liberty-mutual.B7qOU1pd.svg)
15. ![Walmart](https://surrealdb.com/assets/static/walmart.BjDg_Sr8.svg)
16. ![Carrier](https://surrealdb.com/assets/static/carrier.D21gC6NX.svg)
17. ![Saks Fifth Avenue](https://surrealdb.com/assets/static/saks-fifth-avenue.COIDpLSb.svg)
18. ![San Francisco Compute Company](https://surrealdb.com/assets/static/sfcc.B7jlImq4.svg)
19. ![Shield AI](https://surrealdb.com/assets/static/shield-ai.pINZ0KJr.svg)
20. ![Wix](https://surrealdb.com/assets/static/wix.DvHhmoBi.svg)

Back to top

[Version 3.1.6](https://surrealdb.com/releases/3.1#release-3-1-6)

[Indexing](https://surrealdb.com/releases/3.1#3-1-6-indexing) [Live queries](https://surrealdb.com/releases/3.1#3-1-6-live-queries) [Query engine](https://surrealdb.com/releases/3.1#3-1-6-query-engine) [Permissions and auth](https://surrealdb.com/releases/3.1#3-1-6-permissions-and-auth) [GraphQL](https://surrealdb.com/releases/3.1#3-1-6-graphql) [RPC and SDKs](https://surrealdb.com/releases/3.1#3-1-6-rpc-and-sdks) [Storage](https://surrealdb.com/releases/3.1#3-1-6-storage) [Configuration](https://surrealdb.com/releases/3.1#3-1-6-configuration)

[Version 3.1.5](https://surrealdb.com/releases/3.1#release-3-1-5)

[Version 3.1.4](https://surrealdb.com/releases/3.1#release-3-1-4)

[Version 3.1.3](https://surrealdb.com/releases/3.1#release-3-1-3)

[Version 3.1.2](https://surrealdb.com/releases/3.1#release-3-1-2)

[Version 3.1.1](https://surrealdb.com/releases/3.1#release-3-1-1)

[Version 3.1.0](https://surrealdb.com/releases/3.1#release-3-1-0)

[Version 3.1.0-beta.3](https://surrealdb.com/releases/3.1#release-3-1-0-beta-3)

[Version 3.1.0-beta.2](https://surrealdb.com/releases/3.1#release-3-1-0-beta-2)

[Version 3.1.0-beta.1](https://surrealdb.com/releases/3.1#release-3-1-0-beta-1)

[Home](https://surrealdb.com/)

[Releases](https://surrealdb.com/releases)

3.1

SurrealDB 3.1.6 is a reliability rollup on the 3.1 line, backporting fixes that have already landed on the 3.2 and 3.3 lines. It fixes a batch of concurrent…

[Newer release line available: 3.2 →](https://surrealdb.com/releases/3.2)

Released Sep 1, 2026

## 3.1.6

SurrealDB 3.1.6 is a reliability rollup on the 3.1 line, backporting fixes that have already landed on the 3.2 and 3.3 lines. It fixes a batch of concurrent index-build, live-query, query-planner, GraphQL, auth, storage and configuration issues, and adds bounds on client-managed WebSocket transactions. It also backports the fix for one previously published security advisory to the 3.1 line.

The fixes are enabled by default and we recommend that all users on the 3.1 line upgrade.

Unlike earlier 3.1.x patches, v3.1.6 makes two additive on-disk format changes - a live-query cache key on the table catalog record and a checkpoint field on the index build-state record. Upgrading in place from any earlier 3.1.x release is supported and needs no action, but a node that has run v3.1.6 cannot be rolled back to an earlier 3.1.x build, and a `DEFINE INDEX ... CONCURRENTLY` / `REBUILD INDEX` should not span a mixed-version window. See Breaking changes.

*v3.1.6 is a patch release with 1 security fix, 1 improvement, and 14 bug fixes on top of 3.1.5.*

## Security

This release backports the fix for one previously published advisory to the 3.1 line. It ships in `v3.1.6` and is enabled by default.

- **Malformed SurrealML file import can panic and terminate the server process** **Moderate** - [GHSA-jwr6-6444-28xv](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-jwr6-6444-28xv)

## Improvements

- **Bounded client-managed WebSocket transactions.** Transactions opened over the `begin` RPC that are never committed or cancelled, or left behind by an abandoned session, could tie up server resources. New per-connection and per-session caps (`SURREAL_MAX_TRANSACTIONS_PER_CONNECTION` and `SURREAL_MAX_TRANSACTIONS_PER_SESSION`, default 64 each) reject further transactions with a `too_many_transactions` error, and any transactions a session still holds are drained and cancelled when it detaches or resets.

## Bug fixes

### Indexing

- Fixed an orphaned concurrent index build never coming back after an ungraceful restart. If a node died mid-build during `DEFINE INDEX ... CONCURRENTLY`, the durable build state stayed in a building state, so `INFO FOR INDEX` reported it as indexing forever and the index never came online. A periodic engine task now adopts builds whose owner lease has expired and drives them to completion, controlled by `--index-build-resume-interval` / `SURREAL_INDEX_BUILD_RESUME_INTERVAL` (default 30s; `0` disables it).
- Fixed an interrupted concurrent index build rescanning from the beginning instead of continuing where it left off. A `DEFINE INDEX ... CONCURRENTLY` or `REBUILD INDEX` that was interrupted and later taken over restarted its scan from zero each time. The builder now checkpoints its scan cursor durably as each batch commits, so a takeover resumes from the last committed position and keeps the partial index instead of rebuilding it from scratch.
- Fixed a shutdown or memory-pressure interruption durably poisoning a concurrent index build. An interrupted build could be left in a permanent error state that also failed every subsequent write to the table. Commit coordinators now refuse commits with a shutdown error before applying, interrupted builds stay resumable at their last checkpoint, and a build in an error state queues writers rather than failing user writes.

#### Live queries

- Fixed live-query notifications being intermittently and persistently lost under concurrency. The live-query subscriber cache was keyed on a free-floating in-memory version bumped before commit, so a concurrent writer could cache a stale subscriber set and poison delivery from then on. The cache is now keyed on a committed timestamp written transactionally with the `LIVE` / `KILL` row, so the subscriber set a notification uses always matches the committed table state.

#### Query engine

- Fixed the streaming executor's `FETCH` ignoring computed index and key idiom parts (for example `refs[0]`, `refs[$i]` or `obj['key']`), which silently fetched nothing instead of resolving the referenced record. It now evaluates them, matching the legacy executor.
- Fixed four idiom recursion strategies silently truncating instead of erroring on unbounded recursion. Only the `default` strategy raised `IdiomRecursionLimitExceeded`; `collect`, `path`, `shortest` and `repeat` returned quietly truncated results. All five now error consistently, matching the legacy engine.
- Fixed the planner choosing a count-only or keys-only index strategy when the index answered only part of a multi-conjunct `WHERE`. An indexed `COUNT` over `a = x AND b = y` with only `a` indexed counted every `a = x` row. The optimisation is now restricted to index scans that are exact for the whole `WHERE`, falling back to a record scan when a residual conjunct remains.

#### Permissions and auth

- Fixed a `DEFINE PARAM` whose `PERMISSIONS WHERE` predicate dereferences the authenticated record (for example `$auth.admin = true`) failing on the streaming executor with a `DatabaseEmpty` error instead of evaluating the predicate, for both allowed and denied record users. The param's predicate is now evaluated with a database context, matching how a function-call permission already resolves.
- Fixed root-level bearer sign-in failing with `NoSigninTarget`. Root bearer access could be defined and granted (`DEFINE ACCESS ... ON ROOT TYPE BEARER FOR USER`) but signing in with the key was missing its dispatch arm, so it never reached the existing, unchanged root-access validation. (The client half of end-to-end root bearer sign-in ships in the SDKs.)

#### GraphQL

- Fixed a GraphQL mutation silently re-parsing a numeric- or time-looking string inside an object or array field as a SurrealQL literal, so `"9000"` became the integer `9000` and broke coercion against a `TYPE string` field. Such strings are now kept verbatim, while `Datetime` / `Duration` / `Uuid` format checks still apply.

#### RPC and SDKs

- Fixed commit-time transaction conflicts reaching clients as a generic error instead of the structured `TransactionConflict` (wire code `-32009`), so SDK conflict-retry helpers never fired on genuine write conflicts. The conflict kind is now preserved across every commit path (explicit `BEGIN` / `COMMIT`, `USE`-time commit, and implicit auto-commit).

#### Storage

- Fixed the TiKV backend retaining MVCC versions for roughly 1000x longer than configured, and panicking on `VERSION` queries. The TSO physical component was treated as microseconds rather than milliseconds, so `run_mvcc_gc` computed a safepoint far too far back, and packing a microsecond timestamp overflowed when converting to a version. Both now use millisecond semantics; a `VERSION` query against TiKV returns a clean unsupported error rather than panicking.
- Fixed the default (non-versioned) `memory` backend never reclaiming superseded MVCC versions and delete tombstones, so its dirty-key set grew without bound under sustained writes. The background GC worker is now wired up on the memory backend.

#### Configuration

- Restored human-readable size parsing for the `memory_threshold` Kubernetes configmap value. Suffixed values such as `256m` or `1g` were rejected by the configmap path (which parsed a plain integer) and logged a spurious warning, even though the environment-variable path already accepted them. Both paths now share the same suffix-aware parser. This complements the `SURREAL_MEMORY_THRESHOLD` environment-variable fix in v3.1.5.

## Breaking changes

Unlike earlier 3.1.x patches, v3.1.6 makes two additive changes to durable on-disk records:

- The table catalog record (`TableDefinition`) gains a `cache_lives_ts` timestamp, used to key the live-query subscriber cache described above.
- The index build-state record gains a checkpoint cursor for resumable concurrent builds.

Both fields are additive and default cleanly on records written by earlier 3.1.x builds, so **upgrading in place to v3.1.6 needs no action**. The constraint is on going backwards: once a v3.1.6 node has written either record, an earlier 3.1.x binary cannot decode it, so **downgrading to an earlier 3.1.x release is not supported after upgrading**.

In addition, a `DEFINE INDEX ... CONCURRENTLY` or `REBUILD INDEX` writes the new index build-state format, so do not start or leave one running across a mixed-version window during an upgrade. If a concurrent build was interrupted by the upgrade, rebuild the index.

There are no SurrealQL syntax changes and no changes to the on-disk layout of existing record data.

Upgrade or install

## Get SurrealDB v3.1.6

Pick how you want to install or upgrade. SurrealDB Studio can update connected instances in place, or choose a platform below to copy a CLI command for v3.1.6.

SurrealDB Cloud

macOS

Linux

Windows

Docker

Already installed

You can upgrade your SurrealDB Cloud instance to v3.1.6 effortlessly through SurrealDB Studio.

1. Open the [SurrealDB Studio](https://studio.surrealdb.com/current/instances)
2. Select your organisation and instance
3. On the dashboard, click on the "Upgrade" button
4. Your instance will be updated and restarted automatically

[Full install guide](https://surrealdb.com/surrealdb/install)

Released Jun 19, 2026

## 3.1.5

SurrealDB 3.1.5 is a security-focused patch release on the 3.1 line. It addresses a batch of issues surfaced through SurrealDB's internal security review process and external reviewers, alongside fixes to graph recursion, the remote SDK, CLI TLS startup, and memory-threshold configuration parsing.

The security fixes are enabled by default and we recommend that all users on the 3.1 line upgrade.

v3.1.5 contains no SurrealQL surface changes and no catalog or on-disk layout changes, so you can upgrade in place from any earlier 3.1.x release. It does carry **one breaking default change** - file access is now denied unless an allowlist is configured - described in the Breaking changes section below.

*v3.1.5 is a patch release with 5 security fixes and 4 bug fixes across the 3.1 series.*

## Bug fixes

### Query engine

- Fixed collect recursion with a minimum depth greater than 1 losing nodes in cyclic graphs. A node whose only in-range walk passed through an already-visited node could be dropped from the result. Collection and expansion are now tracked separately, so every distinct node reachable by a walk of length within `[min_depth, max_depth]` is collected, matching the legacy compute engine while still bounding traversal on cyclic graphs.

#### SDK and connections

- Fixed Rust-based SDKs failing to decode large query result sets ("Failed to decode fb value"). The FlatBuffers verifier's `max_tables` limit is now sized to the payload length, so large flat result sets and legitimately nested records decode again, while nesting depth stays bounded by a fixed constant so the untrusted-input decode path cannot be driven into a stack overflow.
- Fixed the remote WebSocket SDK hanging when a server message could not be deserialised far enough to recover its request id. Such a message previously was logged and dropped, leaving every awaiting query blocked forever; all currently-pending requests are now failed with the decode error so callers surface it instead of hanging.

#### CLI and TLS

- Fixed remote client CLI commands (`sql`, `import`, `export`, `isready`, and others) aborting on their first TLS handshake. The process-wide rustls crypto provider is now installed for every CLI command before any TLS connection is opened, not just for `start`.

#### Configuration

- Restored human-readable size parsing for `SURREAL_MEMORY_THRESHOLD`. Values with a byte suffix such as `1792mb` or `1g` are honoured again (case-insensitive `b`/`kb`/`kib`/`mb`/`mib`/`gb`/`gib`); a plain byte count still works, `0` disables the threshold, and any other non-zero value is floored to 1 MiB. A regression had caused suffixed values to silently disable the guard.

## Security

This release addresses a set of issues surfaced through SurrealDB's internal security review process and external reviewers. The fix for each ships in `v3.1.5` and is enabled by default. Aside from the file-access default change noted under Breaking changes, no configuration is required.

- **Arbitrary file read via `DEFINE ANALYZER mapper()` filter** **High** - [GHSA-cc8f-fcx3-gpjr](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-cc8f-fcx3-gpjr)
- **SSRF via JWKS URL redirect following in JWT key fetch** **Moderate** - [GHSA-h5rg-8p7f-47g2](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-h5rg-8p7f-47g2)
- **Field-level SELECT permissions bypassed via graph and reference traversals** **Moderate** - [GHSA-hv6h-hc26-q48p](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-hv6h-hc26-q48p)
- **Indexed ORDER BY leaks the value ordering of a SELECT-restricted field** **Moderate** - [GHSA-h4h3-3rfj-x6fq](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-h4h3-3rfj-x6fq)
- **Denial of service via deep operator chains** **Moderate** - [GHSA-jv2j-mqmw-xvv5](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-jv2j-mqmw-xvv5)

## Breaking changes

Catalog and KV on-disk layouts are unchanged from earlier 3.1.x releases, so you can upgrade in place. The item below is a **default change** that can affect deployments relying on the previous behaviour.

### File access is now denied by default

As part of the [arbitrary file read fix](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-cc8f-fcx3-gpjr), file access used by SurrealQL features such as `DEFINE ANALYZER ... mapper('<path>')` is now **denied unless `file_allowlist` is explicitly configured**. Previously an empty allowlist permitted access to any path; the new default denies all file access, mirroring the rest of the capability model (an empty `allow_net` denies all network).

If you rely on `mapper()` or other file-backed analyzer features, configure `file_allowlist` (for example via the `SURREAL_FILE_ALLOWLIST` environment variable) with the directories holding your files before upgrading. Otherwise those definitions will fail with a file-access-denied error.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.5 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released Jun 10, 2026

## 3.1.4

SurrealDB 3.1.4 is a patch release on the 3.1 line. It addresses a permission-enforcement vulnerability in array element-level `SELECT` permissions, alongside a query-planner correctness fix, a permission fix for the experimental file buckets feature, and a build fix for asynchronous Surrealism modules.

The security fix addresses array element-level `SELECT` permissions that could leak denied elements. We recommend that all users on the 3.1 line upgrade.

v3.1.4 is a drop-in upgrade from any earlier 3.1.x release, with no SurrealQL surface changes and no catalog or on-disk layout changes.

*v3.1.4 is a patch release with 1 security fix and 3 bug fixes across the 3.1 series.*

## Security

- **Array element-level SELECT permissions leak denied elements** **Moderate** - [GHSA-8rw6-p7m8-63jp](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-8rw6-p7m8-63jp)

A field permission on an array's elements (for example `PERMISSIONS FOR select WHERE ...` on `items[*]`) expanded the array in ascending index order and removed each denied element with an in-place `Vec::remove`, which shifts every later element down. Cutting forward therefore invalidated the pending indices: a deny-all permission leaked the odd-indexed elements, and a per-element predicate kept or dropped the wrong elements. All affected paths now cut elements in reverse index order, so denied elements are removed by identity and permitted siblings are preserved.

## Bug fixes

### Query engine

- Fixed `type::field('id')` equality not using the record-id point lookup. A query such as `SELECT * FROM item WHERE type::field('id') = item:1` (and the reversed-operand form) is now planned as a `RecordIdScan` point lookup, like a bare `id`, instead of falling back to a full table scan when no user-defined index on `id` exists. Residual conjuncts such as `AND score > 15` are still applied after the lookup.

#### File buckets (experimental)

- Fixed bucket `PERMISSIONS WHERE` predicates not being enforced in the streaming file executor. The streaming executor previously could not evaluate a bucket's `Permission::Specific` expression and fell back to a role-only check, allowing record and guest users to bypass `DEFINE BUCKET ... PERMISSIONS WHERE` clauses on `file::get`, `file::put`, `file::copy` and related functions. The executor now evaluates the bucket predicate with the `$action`, `$file` and `$target` parameters bound, matching the legacy compute path, and `list` operations are checked against the `List` operation rather than `Exists`.

#### Modules

- Fixed building asynchronous Surrealism modules with `surreal module build`. The Surrealism SDK relies on Tokio's `net` feature on the `wasm32-wasip2` target, which currently requires the `tokio_unstable` cfg flag. The build command now generates a `.cargo/config.toml` with the required build flags (leaving any existing file untouched) and sets `RUSTFLAGS` accordingly. Failed `cargo build` and `cargo metadata` invocations now surface the underlying compiler output instead of a generic error message.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.4 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released Jun 3, 2026

## 3.1.3

SurrealDB 3.1.3 is a patch release on the 3.1 line. It focuses on connection and SDK robustness, query-engine correctness, and build fixes across more platforms.

Notable fixes include a cold-start "Session not found" race in the engine router, a graph traversal carrying an inline filter that returned no rows, permission predicates that run their own subqueries, and embedded scripting `fetch()` Blob types. It also restores 32-bit builds and makes indexed k-nearest-neighbour query plans clearer in `EXPLAIN`.

v3.1.3 is a drop-in upgrade from any earlier 3.1.x release, with no SurrealQL surface changes and no catalog or on-disk layout changes. The one platform note is that DiskANN vector indexes now require a 64-bit target.

*v3.1.3 is a patch release with 3 minor improvements and 10 bug fixes across the 3.1 series.*

## Improvements

### Vector search

- **Clearer kNN query plans.** `EXPLAIN` now reports the filter pushed into an indexed k-nearest-neighbour search as a `predicate` attribute on the `KnnScan` operator, mirroring how `TableScan` reports its own predicate. Previously an indexed kNN query with a pushed-down filter was indistinguishable in the plan from one without.

#### Dependencies and storage engine

- **Embedded `surrealmx` storage engine upgraded to 0.22.0.** The upgrade is API-compatible with no query-surface change, and drops a transitive dependency (`ferntree`) as a precaution against a theoretical race-condition risk.
- **Looser `wasm-bindgen` constraint.** `wasm-bindgen-futures` is no longer pinned to an exact version, so projects that depend on the SDK alongside a newer `wasm-bindgen` resolve cleanly.

## Bug fixes

### SDK and connections

- Fixed a cold-start race in the engine router that could surface a spurious "Session not found" error on the first request after connecting.
- Idempotent `USE` entries are now coalesced in the remote replay log, so reconnecting no longer replays redundant namespace and database selections.
- In-flight queries are now cancelled when their WebSocket connection closes, instead of continuing to run after the client has disconnected.

#### Query engine

- Fixed a graph traversal carrying an inline filter returning no results. A query such as `SELECT * FROM person:1->(knows WHERE since > 2020)` now returns the matching edges instead of an empty array.

#### Permissions

- Permission predicates now evaluate as the system. A permission `WHERE` clause that runs its own subquery or graph traversal no longer recursively re-triggers row-level permission checks. Denial behaviour is unchanged.

#### Scripting

- In embedded scripting, the `type` of a `fetch()` response `Blob` is now derived from the `Content-Type` header, matching the Fetch specification.

#### GraphQL

- The GraphQL schema cache now evicts entries deterministically, in first-in, first-out order.

#### Build and platform

- Restored 32-bit builds. `i686`, `armv7`, and other 32-bit targets compile again, with DiskANN now gated to 64-bit targets.
- Fixed a build failure when compiling with only the `protocol-http` feature, without WebSocket support.
- Fixed a panic when running with `tokio-console` enabled. Release artifacts are now built with the configuration that `tokio-console` requires.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.3 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released May 27, 2026

## 3.1.2

SurrealDB 3.1.2 makes table scans with a `WHERE` condition faster, and replaces two unclear error messages with ones that say what to change. It also includes every change in 3.1.1.

Any 3.1 datastore upgrades in place, and there are no SurrealQL changes.

## Improvements

- `[Query]` **Faster filtered table scans.** A `WHERE` condition on a field without an index now does less work to read that field from each record, including nested fields such as `address.city`.

## Bug fixes

### Vector search

- Fixed a KNN query written as `<|k|>` reporting that the operator was nested in `OR` or `NOT` when it was not. The error now says that `<|k|>` is no longer supported and suggests `<|k, EF|>` against an HNSW index or `<|k, DISTANCE|>` for a brute-force search, and a KNN operator that is nested in `OR` or `NOT` gets a separate error. [KNN](https://surrealdb.com/docs/reference/query-language/language-primitives/operators#knn)

### Server

- Fixed a `file://` datastore path failing to start with an unclear message. The error now says that the `file://` scheme is no longer supported and names `rocksdb://` and `surrealkv://` as replacements. [Positional argument](https://surrealdb.com/docs/reference/cli/db/commands/start#positional-argument)

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.2 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released May 27, 2026

## 3.1.1

SurrealDB 3.1.1 lets you set the size of the storage thread pool, and makes some common queries and writes cheaper to run.

A 3.1.0 datastore upgrades in place, and there are no SurrealQL changes.

## Improvements

- `[Storage]` **The storage thread pool size can be set.** `SURREAL_KVS_THREADPOOL_SIZE` sets how many worker threads the in-memory, RocksDB and SurrealKV engines use for storage work, with a minimum of 4. Without it, the pool keeps its default of one thread per CPU core, or 16 threads on hosts with fewer than 16 cores. [Environment variables](https://surrealdb.com/docs/reference/cli/db/environment-variables#runtime-config)
- `[Query]` **Equality checks joined by `OR` on one field are evaluated as one lookup.** In a table scan, a condition such as `WHERE v = 1 OR v = 2` is now checked like `v IN [1, 2]`, so the field is looked up once for each record rather than once for each value.
- `[Storage]` **Lower overhead for RocksDB writes.** Writes and rollbacks that only change a transaction's in-memory buffer now run straight away instead of being passed to the storage thread pool.
- `[SDK]` **Fewer build dependencies for `surrealdb-types`.** The crate no longer compiles `rstest` and its dependencies outside of tests, which shortens a cold build of a project that uses the Rust SDK.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.1 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released May 26, 2026

## 3.1.0

SurrealDB 3.1.0 is the first minor release on the 3.0 line. It is a stability-focused update with a thorough security pass, many correctness fixes, faster read and scan paths, and a single unified observability pipeline.

It also adds a first-party MCP server for AI agents, a DiskANN vector index alongside HNSW, async functions in Surrealism plugins, full `ALTER` coverage for every `DEFINE` statement, distributed trace propagation, a GraphQL overhaul (Apollo naming, cursor pagination, `GRAPHQL_ALIAS`, and expanded filters), and `value::expect` for assertions in SurrealQL. In SurrealDB Enterprise, you also get durable audit logging and slow-query telemetry.

The 3.0 → 3.1 catalog and on-disk layouts are unchanged, so existing 3.0.x deployments can upgrade in place. A handful of operational defaults have been tightened, notably RocksDB readahead, which is now 256 KiB. See Improvements for details.

*v3.1.0 lands 10 highlight features, 46 broader improvements, 108 bug fixes, and 22 security fixes across the 3.1 series.*

## Highlights

### 🤖 First-party Model Context Protocol (MCP) server

SurrealDB now exposes a typed tool surface for AI agents. You can run it locally as a `surreal mcp` stdio subcommand for IDE integrations, or over HTTP at `/mcp` behind the existing authentication middleware.

- Twelve typed tools: `query`, `select`, `create`, `insert`, `upsert`, `update`, `delete`, `relate`, `info`, `list`, `use`, and `run`.
- `read_only_hint` / `destructive_hint` / `idempotent_hint` annotations so MCP clients can prompt before mutating operations.
- Self-describing schema resources at `surrealdb://schema/ns/{ns}/db/{db}[/table/{table}]`.
- New environment variables: `SURREAL_HTTP_MAX_MCP_BODY_SIZE` (default 4 MiB), `SURREAL_MCP_QUERY_TIMEOUT_SECS` (default 60 s), `SURREAL_MCP_MAX_RESULT_BYTES` (default 256 KiB), `SURREAL_MCP_RUN_MAX_ARGS` (default 64), and `SURREAL_MCP_PARAMS_MAX_KEYS` (default 256).

### 📊 Unified observability and monitoring pipeline

Observability is now a single OpenTelemetry pipeline instead of separate community Prometheus and SemConv paths.

- Every signal is recorded once and routed to many exporters: Prometheus text on `/metrics`, OTLP metrics push, OTLP logs push.
- Metric families are reorganised under the `surrealdb.*` scope (statement, query, transaction, RPC, auth, session, network, HTTP, live query, process, storage).
- The `/metrics` endpoint enforces a render-time `PUBLIC_METRICS` allow-list. Anonymous scrapers see only aggregate process gauges, while root-authenticated operators see the full surface.
- Tenant context (`namespace` / `database` / `user`) is carried on every labelled family but filtered out of the public view by family name.
- New `surrealdb.transaction.retries` / `surrealdb.transaction.conflicts` counters fire from the commit path when the storage engine returns a retryable error.
- MCP tool dispatch now emits per-tool request metrics alongside GraphQL, HTTP, and WebSocket surfaces.
- New environment variable: `SURREAL_PROCESS_METRICS_REFRESH_INTERVAL` (default 5 s). Retired: `SURREAL_TELEMETRY_NAMESPACE`, `SURREAL_TELEMETRY_RPC_LIVE_ID`. Repurposed: `SURREAL_METRICS_ENABLED`.
- This is a breaking change for existing dashboards. See the [observability documentation](https://surrealdb.com/docs/manage/observability) for the migration table and the full new surface.

### 🧭 Distributed trace-context propagation

Inbound `traceparent` / `tracestate` headers are now respected on every transport, so OTEL-aware clients see traces extend continuously through the server.

- HTTP: `traceparent` is extracted from request headers and parented onto the existing `request` span. This applies to `/rpc`, `/sql`, `/key/*`, `/graphql`, `/health`, and every other route.
- WebSocket: per-message trace context lives in the RPC envelope (`trace_context` field, matching the W3C name), so a long-lived connection can serve many independent SDK operations under their own traces instead of bucketing them all under the first one.
- When `SURREAL_LOG_OTEL_LEVEL` is unset, the four user-facing spans (request, rpc/call, rpc.execute, executor) surface out of the box. Operators who want deep nested core instrumentation can set `SURREAL_LOG_OTEL_LEVEL=trace`.
- Behaviour is unchanged for SDKs that don't yet emit propagation context. Requests without a `traceparent` continue to produce fresh root spans.

### 🧭 DiskANN approximate-nearest-neighbour index

A new vector index type sits alongside HNSW under the same KNN query operator, with an overhauled warm-lookup path that benefits both implementations.

- New index syntax:

  ```sql
  DEFINE INDEX pts_embedding_diskann ON pts FIELDS embedding
      DISKANN DIMENSION 4 DIST EUCLIDEAN TYPE F32 DEGREE 16 L_BUILD 64;

  SELECT id, label FROM pts
  WHERE embedding <|2,64|> [0f, 0f, 0f, 0f];
  ```

  `<|K, EF|>` queries DiskANN through the same operator used for HNSW.
- Both DiskANN and HNSW gain `TYPE F16`, `TYPE U8`, and `TYPE I8` element types, plus `DISTANCE INNER_PRODUCT` and `DISTANCE COSINE_NORMALIZED` metrics. Add `HASHED_VECTOR` to the index definition if vector-to-document mappings should be stored through vector hashes instead of full vectors.
- The ANN lookup path was reworked with process-local caches for graph payloads, vector-to-document mappings, and doc-id lookups. KV remains the source of truth. Cache misses fall back to storage, and uncommitted writes never poison the cache.
- `DEFINE INDEX` on an ANN index now drains pending vectors before returning, so a freshly-defined index is immediately usable.
- **Concurrent builds and kNN at release.** DiskANN compactions coordinate the cache and graph lock through commit so concurrent `DEFINE INDEX … CONCURRENTLY` and KNN queries do not race. HNSW steady-state kNN avoids serialising on graph reload when the in-memory graph already matches KV. Intermittent `DiskANN KNN search failed` errors from 3.1 betas are addressed [(PR #7318)](https://github.com/surrealdb/surrealdb/pull/7318).

### ⚡ Predicate prefilter and scan-path performance work

A series of changes lift the new executor's scan throughput well above 3.0, on both cold and warm data.

- **Predicate prefilter on every KV scan.** Supported `WHERE` predicates are evaluated against raw key/value bytes before the record is decoded, skipping rows that are provably false. `EXPLAIN` reports whether the prefilter applied and which predicates were pushed down.
- **K-way merge for `WHERE col IN [...] ORDER BY other LIMIT N`.** `UnionIndexScan` now merges per-IN-list branches by the composite-index suffix column, so an outer `LIMIT` can stop after ~`LIMIT` rows globally instead of materialising every match.
- **Composite array-element indexes for `CONTAINS` / `INSIDE` and `CONTAINSANY` / `ANYINSIDE`.** Queries like `WHERE tags CONTAINS 'x' ORDER BY age DESC LIMIT N` now plan as bounded index scans with `LIMIT` pushed into the scan instead of a full-range read plus heap sort. `CONTAINSALL` / `ALLINSIDE` still keep a residual filter above the union until a follow-up lands.
- **Single-scan `->edge->vertex` graph traversals.** Vertex-side adjacency keys now embed the target `(table, id)` after `RELATE`, so plain `SELECT ->likes->person FROM person:…` can be served by one range scan. Legacy keys remain readable, and re-`RELATE` lazily upgrades the format. Edge tables with non-`FULL` `SELECT` permissions stay on the two-scan path.
- **Lower default RocksDB readahead for better NVMe scan throughput.** `SURREAL_ROCKSDB_MAX_AUTO_READAHEAD_SIZE` is now 256 KiB on every deployment, replacing RAM-tiered defaults that reached 4 MiB. Larger readahead was often split into multiple IOs with per-IO overhead on typical NVMe devices.
- **Per-row clone elimination on the projection hot path.** The streaming executor now moves row payloads through `Compute` and projection instead of deep-cloning them per row.
- **Hash-keyed `GROUP BY` aggregation.** The aggregate state map is now hash-keyed, cutting group-key allocations on large grouped queries while preserving key-sorted output ordering.

### 💾 Memory optimisations on the value layer

This release includes memory work on the value layer: a small-string-optimised `Strand` type, a new `surrealdb-collections` crate with `VecMap`/`VecSet`, and reduced per-transaction overhead on the hot path.

- `Strand` is a 24-byte small-string-optimised immutable string type, routed through core hot-path strings: `Value::String`, `Object` keys, `TableName`, and `RecordIdKey::String`.
- `Strand` packs into the same 24 bytes as `String` via a `repr(C)` SmolStr-style tagged union, with an inline variant storing strings up to 23 bytes with zero heap allocation.
- A heap variant uses `Arc<str>` so clones are a refcount bump rather than a copy.
- Object entries and `Value::String` shrink by 8 bytes each, which compounds at document scale.
- The wire format is unchanged. `Strand` serialises identically to `String` through `serde`, `Revisioned`, and `storekey`.
- A companion `Strand::new_static` constructor lets compile-time-known strings in core skip allocation entirely.

### ⚙️ Async functions in Surrealism plugins

Plugin authors can mark functions exported through `#[surrealism]` as `async fn` and `.await` host imports directly (storage, network, queries).

- Modules can now use async crates like `reqwest` and `sqlx` that internally depend on `tokio::spawn` for connection pooling and timers.
- The host runtime is initialised once per module instance and persists across invocations on pooled controllers, so there's no per-call setup cost.
- The accompanying `surrealism::demo` crate gains an `async fn fetch_pokemon` example that exercises WASI socket access under `allow_net = ["127.0.0.1"]`.

The broader Surrealism overhaul (WASI Preview 2, WIT-typed host/guest contract, FlatBuffers boundary, attached read-only filesystem, two-engine epoch model) is included in 3.1. See the original beta.1 entry for the full surface change.

### 🛠 Expanded `ALTER` coverage

Every `DEFINE` statement now has a matching `ALTER` counterpart.

- Added `ALTER` support for `EVENT`, `PARAM`, `BUCKET`, `ANALYZER`, `FUNCTION`, `USER`, `ACCESS`, `CONFIG`, and `API`.
- Each follows the same property-by-property pattern as the existing `ALTER TABLE` / `FIELD` / `INDEX` statements.
- Individual properties on a definition can be modified without re-specifying the whole definition via `DEFINE … OVERWRITE`.
- `ALTER` resource names and table targets accept bound parameters, matching the `DEFINE` / `REMOVE` parameterisation story.
- `ALTER PARAM` does not support `DROP VALUE` (a param without a value is meaningless). `ALTER EVENT` uses `DROP ASYNC` to revert to synchronous mode. `ALTER ACCESS` does not allow changing the access type itself.

### 📦 Durable distributed index build coordination

Concurrently built indexes now keep their build state in the catalog rather than in process memory, and every node coordinates through it.

- On multi-node deployments sharing a storage engine, all nodes now agree on which node owns the build, which writes the in-progress index admits, when replay is complete, and when the index becomes query-eligible.
- `INFO FOR INDEX` reflects the durable state.
- The same change fixes a class of issues where COUNT indexes could overcount when the initial build ran on multiple nodes concurrently.
- No SurrealQL surface change.

### 🔐 Audit logging and slow-query telemetry (Enterprise)

SurrealDB Enterprise ships a new audit-logging and slow-query pipeline alongside the unified observability surface.

- A queued, worker-backed `audit_log` module records authenticated operations to a configurable sink, with a redaction layer that strips sensitive fields before serialisation.
- Slow-query detection runs alongside the audit pipeline and emits structured records for queries that exceed the configured threshold.
- Both pipelines share a size-rotated, SHA-256 hash-chained, optionally fsync'd file sink in append-only `0600` mode. Slow-query is essentially the audit pipeline with a duration gate at the front of the observer.
- Configuration via 13 `SURREAL_AUDIT_*` and matching `SURREAL_SLOW_QUERY_*` environment variables: sink kind, file path, rotation, fsync cadence, hash chaining, SQL inclusion, redaction (tables / regex / literals), queue capacity, overflow strategy, OTLP export.
- See the [observability documentation](https://surrealdb.com/docs/manage/observability) for the full operator surface.

## Improvements

### RocksDB and storage engine

- **Upgraded RocksDB to 11.0.0 and tuned the engine.** Enabled prefix extractor for keyspace-aware bloom filters, aligned compaction readahead to disk block size, and enabled blob-file separation by default for large-value workloads. Scaled defaults for constrained environments while large deployments (≥16 GiB memory) keep the previous defaults via tiered scaling.
- **Tuned RocksDB scans and grouped-commit.** Read-only transactions now bypass the merge layer in scan and count paths, removing overhead on every step. Large scans are offloaded to the storage threadpool using improved byte-based sizing. Fixed shutdown and lost-wakeup edge cases in grouped-commit coordinators.
- **Full-keyspace compaction to bottommost on `ALTER SYSTEM COMPACT`.** `ALTER SYSTEM COMPACT` and per-resource `ALTER {DATABASE,NAMESPACE,TABLE} COMPACT` now land outputs at L6 with bottommost-level Zstd compaction. Tightened auto-compaction trigger defaults. New `SURREAL_ROCKSDB_PERIODIC_COMPACTION_SECONDS` (default 3600, 0 disables) bounds version overhang and tombstone accumulation on cold ranges. Universal compaction is now a first-class option (still off by default) with configurable knobs.
- **Cleaner RocksDB shutdown sequence.** Optional full-keyspace compaction (`SURREAL_ROCKSDB_COMPACT_ON_SHUTDOWN`, default `false`), bounded `wait_for_compact` (`SURREAL_ROCKSDB_SHUTDOWN_WAIT_FOR_COMPACT_SECONDS`, default 60 s), and deterministic background-thread teardown. Errors in the shutdown path are logged and continued rather than propagated.
- **Optional RocksDB scan checksum verification.** New `SURREAL_ROCKSDB_SCAN_VERIFY_CHECKSUMS` flag, default `true` (existing behaviour). Setting it to `false` skips CRC32C verification on first-read blocks during scans and counts, trading a small amount of integrity checking for cold-scan throughput on trusted storage.
- **RocksDB user-defined timestamps for versioned reads.** `SELECT … VERSION`, `INFO FOR DB / TABLE VERSION …`, graph and reference traversals, the `FETCH` clause, and inherited inner subqueries are isolated from the LRU transaction cache so historical reads cannot pollute current-time reads. `INFO FOR ROOT` and `INFO FOR NS` now also accept `VERSION`.
- **SurrealKV updated to 0.21.2**, picking up fixes from [(PR #7303)](https://github.com/surrealdb/surrealdb/pull/7303). It also raises the maximum memtable size for large transactions.
- **Memory backend versioning parity.** `memory://?versioned=true` (and `mem://`) is parsed correctly at startup. `SELECT … VERSION` on a non-versioned memory datastore returns the same error as RocksDB and SurrealKV. Default `surreal start` no longer enables versioning implicitly.

#### Performance and executor

- **Reduced per-transaction overhead on the hot path.** Built-in function registry is now shared across transactions instead of rebuilt on every query. Change-feed writer is allocated lazily. Per-transaction cache sizing is tuned for transaction scope rather than defaulting to a large multi-shard allocation.
- **Capped the scanner's initial batch size using the query `LIMIT`** so that small-`LIMIT` queries don't fetch more rows than they will ever return.
- **Added a sync fast-path for `PhysicalExpr` evaluation in scan filters**, avoiding async overhead when no IO is required.
- **Avoided cloning the document on `SELECT *` projection** where the source row can be passed through unchanged.
- **Replaced the global HTTP client cache** with a single `HttpClient` instance per `Datastore`, exposed via `Context::http_client`.
- **Configurable scan batch size.** Promoted the scan `BATCH_SIZE` constant to `SURREAL_SCAN_BATCH_SIZE` (default 1000).
- **In-memory performance improvements** via SurrealMX switching to ferntree [(PR #74)](https://github.com/surrealdb/surrealmx/pull/74), a concurrent B+tree with optimistic lock coupling.
- **Optimised internal `Value` wire encoding (`revision` 0.23.0).** Planner walks can skip or seek without fully deserialising large records. User-visible serialisation through RPC and export is unchanged.

#### Observability and operations

- **OTLP exporters now build with TLS** (`tls` + `tls-roots` features), using the platform's native root store via `rustls-native-certs`. `rpc.request_id` is emitted as a string identifier. The OTLP runtime is driven by an async runtime so exporters no longer block on a dedicated thread.
- **Made datastore initialisation retry resilient to hung attempts.** Each retry attempt is wrapped in its own timeout that increases linearly. The global retry budget is raised from 60 s to 120 s. Failures are logged with a per-task label, and exhaustion returns a descriptive error.
- **Bounded shutdown background work** to mirror the bounded-startup retry behaviour, so a stuck task can no longer indefinitely delay datastore shutdown.
- **MCP request instrumentation** added to the community OpenTelemetry pipeline, with error classification distinguishing client errors from server errors.
- **Auto-detect TTY for the console log output** and respect the `NO_COLOR` environment variable. ANSI escape codes are now only emitted when both stdout and stderr are TTYs.
- **Pluggable live-query broker for clustered deployments.** `Datastore` accepts an optional `MessageBroker` so a composer can forward live-query notifications to the node that owns each subscription when writes commit on a different node than the subscriber's WebSocket.

#### Functions and SQL surface

- **Added `value::expect` method.** Assert a predicate on a value via a closure and pass the original value through on success. This is useful in method chains alongside `value::chain`. On failure, throws with an optional message.
- **Added `REMOVE CONFIG [IF EXISTS] GRAPHQL | API | DEFAULT`** to mirror `DEFINE CONFIG`.
- **Extended GraphQL schema generation** to support fields whose type is a literal (record / object / array literal types). Added GraphQL Subscriptions and root-level field comments.
- **Added `encoding::json::encode` and `encoding::json::decode`** for serialising SurrealDB values to and from JSON strings, and added support for UTF-16 surrogate-pair escape sequences in the JSON parser (gated on a `json_string_escapes` parser setting).
- **Use `serde_json` to parse external JSON in `http::*` functions** so valid JSON responses containing escaped forward slashes (`\/`) are no longer rejected.
- **Added `--tables-exclude` flag to `surreal export`** for excluding specific tables from an export.
- **Allow `surreal validate` to read input from stdin**, so SurrealQL can be piped in for syntax checking without writing to a file first.
- **Configurable CORS allow list** for the HTTP server.
- **Reject `REMOVE ANALYZER` while a full-text index uses it**, with a clear error naming the offending index and table.
- **`REMOVE MODEL` now deletes the model file** from the configured object store rather than leaving the `.surml` artifact orphaned.
- **Tightened numeric input handling on standard library functions.** Functions such as `rand::id`, `array::repeat`, `string::repeat`, `duration::from_*`, and `<bytes> […]` now reject negative or out-of-range inputs with a clear error rather than silently wrapping.
- **Strict array-index conversion.** Array indexing now treats negatives, NaN, infinities, fractional values, and out-of-range magnitudes as out-of-bounds and returns `NONE`. Integer-valued floats and decimals remain valid indices.

#### SDK and types

- **Expanded the `surrealdb-types` SDK and derive surface.** Added `#[surreal(rename_all = "...")]` on enums and structs, `#[surreal(wrap)]` for transparent wrapper types, more `SurrealValue` implementations, serde interop on the `Value` type, restored query chaining on the `Query` builder, and several missing crate exports.
- **Added a builder pattern to `Datastore`** for properties that cannot be changed after construction, replacing post-construction setters.
- **Added `surrealdb-collections` workspace crate** with `VecMap` and `VecSet`. These are vec-backed, ordered map and set types for small-collection workloads. `Object` and `Set` in `surrealdb-core` now use these types.
- **Removed several process-wide configuration globals** in `surrealdb-core` so configuration is now carried through `Datastore` / `Context`.
- **SDK tolerates servers denying the `version` RPC** during connect (`--deny-rpc version`).
- **Improved error communication.** Type errors in arithmetic / extend operators now report only the type name rather than the raw operand.

#### GraphQL

This release includes a consolidated GraphQL pass that standardises the auto-generated schema on Apollo conventions, adds Relay-style cursor pagination, and fixes a batch of schema-generation and filter bugs. See [GraphQL overview](https://surrealdb.com/docs/learn/querying/graphql/overview) and [`DEFINE CONFIG GRAPHQL`](https://surrealdb.com/docs/reference/query-language/statements/define/config#define-config-graphql).

#### Schema naming (Apollo-only)

There is no longer a `NAMING DEFAULT|APOLLO` switch. Apollo-style names are the only generated shape:

- **Fetch one record:** `person(id: ID!)` (was `_get_person(id:)`).
- **List records:** `people(filter, where, order, limit, start, version)` with a pluralised table name (was `person(...)` on the list query).
- **Aggregate:** `people_aggregate(filter, groupBy, …)` (was `person_aggregate`).
- **Mutations:** `createPerson` / `updatePerson` / `upsertPerson` / `deletePerson`, with bulk forms `createPeople` / `updatePeople` / etc. (was `createManyPerson` and similar).
- **Object and input type names** keep the SurrealQL source name so `record<table>` references stay stable. Field names mirror SurrealQL idioms unless overridden.
- Tables whose names are already plural (`likes`, `follows`) keep the legacy `_get_<name>` / `<verb>Many<Cap>` shape so singular and bulk mutation names stay distinct.
- Two tables that collapse to the same query name abort schema generation with a clear error suggesting `GRAPHQL_ALIAS`.

#### `GRAPHQL_ALIAS` and `GRAPHQL_DEPRECATED`

New optional clauses on [`DEFINE FIELD`](https://surrealdb.com/docs/reference/query-language/statements/define/field), [`DEFINE TABLE`](https://surrealdb.com/docs/reference/query-language/statements/define/table), and [`DEFINE FUNCTION`](https://surrealdb.com/docs/reference/query-language/statements/define/function):

```surql
DEFINE FIELD first_name ON person TYPE string
    GRAPHQL_ALIAS "firstName";

DEFINE TABLE customer_account
    GRAPHQL_ALIAS "Customer";

DEFINE FUNCTION fn::get_user($id: string) -> user { ... }
    GRAPHQL_ALIAS "getUser";

DEFINE FIELD old_score ON player TYPE int
    GRAPHQL_DEPRECATED "Use `rank` instead";
```

Aliases must be valid GraphQL `Name` tokens. Invalid aliases fall back to the auto-derived name. `INFO FOR DB` and export round-trip the clauses. Deprecation reasons surface in field descriptions as `[Deprecated: …]` until the schema builder can emit the `@deprecated` directive on dynamic fields.

#### Cursor pagination and batched HTTP

Each table gains a Relay-style connection field, for example `peopleConnection(first, after, last, before, filter, where, order, version)`, returning `{ edges { cursor node { … } }, pageInfo, totalCount }`. Cursors are URL-safe base64 of the record id. `first` / `last` default to 20 and cap at 1000. `totalCount` runs only when the client selects it. Offset pagination via `limit` / `start` on the list query remains available.

Posting a JSON **array** of `{ query, variables, … }` envelopes to the GraphQL HTTP endpoint returns a parallel array of responses (batched requests).

#### Filters and aggregations

- **Vector similarity and KNN filters** on numeric array fields (for example `array<float>` embeddings). Per-field filter inputs accept `similarity` and `nearest` (maps to SurrealQL `<|k, distance|>`). Distance metrics include cosine, Euclidean, Manhattan, Hamming, Jaccard, Chebyshev, and Pearson. Includes the vector-similarity portion of [(PR #7312)](https://github.com/surrealdb/surrealdb/pull/7312).
- **Full-text search filter** via `matches` on string fields, translating to SurrealQL `@@` (requires `DEFINE INDEX … SEARCH ANALYZER …`).
- **Table aggregation** via `<plural>_aggregate(filter, groupBy)`, exposing `count`, per-numeric-field min/max/sum/avg, and optional `groupBy`.
- **Function-call filters** via `call` on every field filter input (`string::len`, `vector::*`, user `fn::…`).
- **`id` filters:** `id: { in: […] }`, `gt` / `gte` / `lt` / `lte`, and `range: { from, to, inclusive }`. [(PR #4555)](https://github.com/surrealdb/surrealdb/pull/4555).
- **Relation `count` in `WHERE`:** relation fields expose `count` on the table filter. For example, `people(filter: { sent: { count: { gt: 5 } } })` compiles to `count(->sent) > 5`. [(PR #4554)](https://github.com/surrealdb/surrealdb/pull/4554).

#### Other GraphQL fixes

- **Schema cache** now keys on a content hash of catalog entries, so DDL changes appear on the next GraphQL request. [(PR #6942)](https://github.com/surrealdb/surrealdb/pull/6942).
- **`TYPE { bar: record<foo> }`** object literals generate nested GraphQL objects instead of failing at schema build. [(PR #7034)](https://github.com/surrealdb/surrealdb/pull/7034).
- **`array<record<T>>` filters and mutation inputs** use valid GraphQL names. Nested-object fields no longer leak dots, and mutation inputs for record arrays map to `[ID!]`. [(PR #4999)](https://github.com/surrealdb/surrealdb/pull/4999).
- **`record<T>` mutation inputs are `ID` everywhere**. Output object types no longer leak into input position.
- **`COMPUTED` and `READONLY` fields** are omitted from `Create*` / `Update*` / `Upsert*` inputs. Pre-computed `DEFINE TABLE … AS SELECT …` views no longer expose create/update/delete mutations.
- **Regex string filters** regression-tested [(PR #5078)](https://github.com/surrealdb/surrealdb/pull/5078).
- **Added support for aliases** for tables, functions, and fields [(PR #4537)](https://github.com/surrealdb/surrealdb/pull/4537)
- **Updated the naming convention** for GraphQL names [(PR #4552)](https://github.com/surrealdb/surrealdb/pull/4552).

## Bug fixes

### surrealdb-core compilation time

- The issue causing much longer compile times for surrealdb-core since SurrealDB 3.0 has been identified. 3.1 includes fixes that have vastly reduced compile times when compiling surrealdb-core or adding it as a dependency. More fixes are planned for upcoming 3.1.x releases.

#### Query Engine

- `NONE.*`, `NULL.*`, scalar `.*`, and geometry `.*` now return `NONE` in the new streaming planner, matching the legacy executor, instead of wrapping the value in a single-element array. This fixes the Go SDK `cannot decode array into <StructType>` error when an `option<record<…>>` field was `NONE` and the query expanded it with `.*`. Mixed arrays also now pass non-record elements through unchanged [(PR #7143)](https://github.com/surrealdb/surrealdb/pull/7143).
- Fixed `$parent` resolving incorrectly inside graph `WHERE` clauses in nested subqueries. `$parent` now refers to the current `SELECT`'s row rather than an outer subquery's parent.
- Fixed `FROM ONLY` not being propagated through fused graph lookup chains, which previously produced incorrect results from constrained graph traversals.
- Fixed `ORDER BY` (and `WHERE`) being silently ignored in subqueries with graph traversal sources like `FROM $parent->edge->target`.
- Fixed bare field paths in a subquery's `FROM` clause evaluating to `NONE` because the source operator had no current value in correlated-subquery context.
- Fixed `$parent` not being bound during `iterator.prepare`, which caused correlated subqueries using `FROM $parent->…` to see `$parent` unset while select targets were being planned.
- Fixed `$parent` resolution in materialised view definitions on the streaming path so view materialisation now sees the same parent context as the legacy executor.
- Fixed `LET` bindings in planned bodies not being visible to `IF` / `FOR` blocks that fall back to the legacy compute path.
- Fixed `$auth` and `$session` returning `NONE` in queries executed inside a client-side transaction.
- Fixed duplicate constant values being omitted in the streaming executor (e.g. `false AS isLiked, false AS isLocked` collapsing to a single column).
- Fixed nested `.*` destructure returning `NONE` for record links. `DestructureField::All` now fetches the linked record before expanding.
- Fixed `SELECT` alias shadowing the source field of the same name on the projection fast path.
- Preserved alias idiom structure in streaming-executor projections so that `AS foo.bar` continues to nest into `{ foo: { bar: _ } }` while `` AS `foo.bar` `` stays as a flat key.
- Fixed `??` (null coalescing) and `?:` (ternary condition) operator precedence binding tighter than arithmetic. `$ctx.limit ?? 2 ** 31 - 1` now parses as `$ctx.limit ?? (2 ** 31 - 1)`.
- Fixed `value::diff` and `value::patch` returning `Function 'value::…' requires async execution` when invoked via method syntax.
- Fixed view materialisation crashing when the source `SELECT` aliased the `id` field.
- Fixed coercion error messages omitting the position of the offending element on large inputs.
- Improved error message when a `LET` statement appears in a non-block expression position.

#### Query Planner

- Fixed `WHERE` filters on indexed fields silently omitting `COMPUTED` fields from evaluation. Indexed scans now resolve computed fields through their pipelines, matching table scans.
- Fixed records with `NONE` / `NULL` fields being silently excluded from `ORDER BY` results when the sort key was on a unique compound index (`REBUILD INDEX` is required to fix existing indexes).
- Fixed `SELECT VALUE` with `ORDER BY` ignoring the sort order when querying from an array source.
- Fixed `ORDER BY DESC` with a variable `LIMIT` inside nested `IF` / `LET` blocks inside a function producing wrong results.
- Fixed `ORDER BY` on edge-table queries with record-link source fields returning unsorted output.
- Fixed `KNN <|K, DISTANCE|>` queries bypassing the HNSW index when the distance matches the index configuration.
- New-executor index analyzer: detects contradictory range bounds and short-circuits to a zero-row scan, merges same-side bounds, short-circuits `WHERE field IN []`, and warns when a `WITH INDEX` hint matches no candidate instead of silently falling back.
- Indexed `count()` / `IndexCountScan` now refuse plans when the `WHERE` clause touches a field governed by non-`Full` SELECT permissions.
- Composite `CONTAINSANY` / `ANYINSIDE` `UnionIndexScan` plans no longer strip predicates on fields with restricted SELECT permissions.

#### Indexes

- Made full-text, COUNT and HNSW compaction SSI-safe so concurrent index activity no longer drops work committed after the compaction snapshot. Compaction plans are bounded to fixed-size batches.
- Fixed concurrent index conflicts and reduced overhead during full-text concurrent builds.
- Fixed cascade-delete index bugs that caused COUNT-index drift, ghost UNIQUE-index entries on relation tables, and phantom UNIQUE-index entries when `IN` / `OUT` records were deleted before the relation.
- **Graph and versioned-read permission enforcement.** Vertex-delete cascades now run edge deletes with the caller's permissions. The `->edge->vertex` single-scan fast path is declined when a `VERSION` clause is present or the edge table's `SELECT` is not `FULL`. Versioned `SELECT` now applies historical field permissions and params.
- Fixed transient transaction conflicts at commit time dropping in-flight background index work.
- Catalog ID sequences now seed safely on the first boot after upgrade, preventing ID collisions with existing catalog entries.
- Fixed a memory leak where `DEFINE INDEX` would pin the entire `Datastore` until process exit [(PR #7304)](https://github.com/surrealdb/surrealdb/pull/7304).

#### View

- Grouped view writes now emit changefeed deltas and trigger LIVE-query notifications. Previously, writes to a `DEFINE TABLE … AS SELECT … GROUP BY …` view skipped these hooks.

#### Live Queries

- Terminate live queries on session invalidation and TTL expiry. The dispatcher also skips notifications for expired sessions.
- Remove live-query registrations when the authenticated principal changes on a session (see [GHSA-4m82-p8cx-f94j](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-4m82-p8cx-f94j) under Security).
- `REMOVE TABLE` now bumps the datastore live-query cache version, so subscriptions cannot survive a drop and `DEFINE TABLE` with the same name.

#### Functions

- `value::diff` followed by `value::patch` now round-trips correctly on top-level scalars [(PR #7239)](https://github.com/surrealdb/surrealdb/pull/7239).
- Empty-group sentinels for aggregate functions are now consistent with the corresponding scalar functions. `time::min` / `time::max` return `NONE` for empty groups, and `math::variance` / `math::stddev` return `NaN` for empty groups [(PR #7301)](https://github.com/surrealdb/surrealdb/pull/7301).
- Materialised aggregate views now compute `math::pow` against the `sum` accumulator and `time::min` via the correct aggregate.
- Fixed `infinity` output and added a non-abbreviated constant path so that the constant is preserved on round-trip.
- Fixed user-duration structure so that durations defined and stored on user definitions round-trip correctly.
- Restored the table-membership check on `type::record(rid, table)` when arg1 is a `RecordId` and arg2 is a `String`.

#### Types / SDK

- Fixed stack overflow on recursive `SurrealValue` types so deeply nested or self-referential type definitions no longer crash on encode / decode.
- Fixed `SurrealValue` derive macro emitting `r#type` instead of `type` for raw-identifier struct fields.
- Fixed a copy-paste bug in `prepare_array` that made the lookup-optimisation branch dead code for edge traversals inside array `FROM` clauses.
- Fixed JSON-patch `remove` operation on lists.
- Fixed `CREATE` failing for fields typed as `set<…>`.
- Fixed negative `FETCH` array indices wrapping to a huge `usize` instead of returning `NONE`.
- Local engine `db.run` now validates the function name as an identifier, preventing non-identifier names from being forwarded verbatim.

#### Storage

- **Memory `?versioned=` enforcement.** `VERSION` / point-in-time reads on `memory://` without `?versioned=true` now return the same error as RocksDB and SurrealKV.
- Fixed long server non-responsiveness when a client cancels `surreal export` mid-stream.
- Filesystem bucket keys are now preserved exactly as supplied. The `lowercase_paths` URL option for filesystem buckets defaulted to `true`, silently folding keys to lowercase on write. The default is now `false` [(PR #7309)](https://github.com/surrealdb/surrealdb/pull/7309).
- `file::list({ limit: -1 })` now rejects negative limits instead of silently treating `-1` as unbounded.
- Tightened the filesystem bucket allowlist. `is_path_allowed` now requires UTF-8 validity and a component-boundary prefix match. Object keys containing `..` segments are rejected.

#### RPC / WebSocket

- Respond with a failure when an in-flight WebSocket request is cancelled rather than silently dropping it.
- Propagate CBOR encode errors instead of panicking on the RPC response path.
- `/key` `POST` / `PUT` / `PATCH` handlers now parse the request body as a single SurrealQL value and bind it to `$data` instead of running it through `Datastore::execute` ahead of the fixed CRUD statement.
- `/key` bodies must be an **inert value expression** (literals, `$param`, constants such as `math::PI` only). Executable forms are rejected, closing arbitrary SurrealQL execution on deployments that expose only `/key`.

#### Transactions

- Surface unified `Cannot COMMIT: …` errors when an explicit `COMMIT` after a previous statement abort fails, instead of returning only the first statement error.

#### Auth

- Fixed `id` resolving to `NONE` inside a table's `FOR select WHERE … permission` clause when a record was created and selected in the same statement.
- Fixed user-defined function `PERMISSIONS` clauses being evaluated for system-level users (`root`, namespace, database).
- Live-query computed-field permissions are now applied after the computed fields are populated.
- `ALTER API` now recomputes the caller's IAM envelope from current privileges.
- GraphQL `fn_*` resolvers now read the session from the per-request GraphQL context instead of closing over the schema-generation-time session.
- `USE NS` / `USE DB` no longer auto-creates namespaces or databases without the same authorization as `DEFINE NAMESPACE` / `DEFINE DATABASE`.

#### Events

- Fixed events defined on a vertex table incorrectly firing for related edge-table records during cascade deletion.

#### Logging

- Fixed JSON log format being unused even when configured.
- Fixed ANSI escape codes leaking into JSON log output and added per-stream TTY detection for mixed-redirect scenarios.

#### CLI

- Prevent panic on systems with restricted `/proc` access where `System::total_memory()` returns 0.
- **`--client-ip=forwarded`.** Parses the RFC 7239 `Forwarded` header and uses the `for=` identifier from the first forwarded-element as the request client IP.
- **FIPS build option (Enterprise).** `surrealdb-server` gains a `fips` Cargo feature that routes TLS through the AWS-LC FIPS module.

#### Imports / Exports

- Fixed re-importing an exported database failing with "field already exists" errors. Export now emits `DEFINE FIELD OVERWRITE`.

#### GraphQL

- **Reserved-word and backtick-quoted field names** no longer leak into the generated schema, fixing introspection failures in strict clients.
- **Nested array types deeper than three levels** now collapse to the `JSON` scalar in the generated schema instead of exceeding the standard introspection limit.
- **Pre-execution HTTP errors** now return a spec-compliant JSON body with `Content-Type: application/json`, instead of a plain-text 400.

## Security

This release addresses a substantial batch of issues surfaced by SurrealDB's internal security review process and external reviewers. Most are reachable only by an authenticated caller and require existing privileges. A couple are pre-auth DoS or capability-bypass conditions. The fix for each of these issues ships in `v3.1.0` and is enabled by default. No configuration is required.

- **HTTP `/rpc` `sessions` method leaks attached session UUIDs** **High** - [GHSA-5qfp-32cf-69jh](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-5qfp-32cf-69jh)
- **HTTP `/rpc` session race condition allows privilege escalation** **High** - [GHSA-4vgr-h27g-cf9p](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-4vgr-h27g-cf9p)
- **Denial of Service in JSON parser due to nested objects** **High** - [GHSA-q729-696q-g9pq](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-q729-696q-g9pq)
- **Unauthenticated remote DoS via malformed RPC `use` call** **High** - [GHSA-wjjj-24cx-f28g](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-wjjj-24cx-f28g)
- **Denial of Service via nested type annotations** **Moderate** - [GHSA-q8qp-67f9-wr3f](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-q8qp-67f9-wr3f)
- **Scraping a table with no available permissions to the current auth level** **Moderate** - [GHSA-98fx-66cf-fc7c](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-98fx-66cf-fc7c)
- **Graph traversal bypasses table SELECT permissions** **Moderate** - [GHSA-vjjx-rfw4-rmfc](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-vjjx-rfw4-rmfc)
- **Authorization bypass via composite record-id paths** **Moderate** - [GHSA-6vg3-hgrw-p5gf](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-6vg3-hgrw-p5gf)
- **Malicious LIVE query can block writes on the watched table** **Moderate** - [GHSA-4v76-cw68-4vc9](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-4v76-cw68-4vc9)
- **Authorization bypass in `KILL` statement allows termination of other users' live queries** **Moderate** - [GHSA-gcwr-5mrf-fvch](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-gcwr-5mrf-fvch)
- **Pre-auth memory amplification via unbounded `/sql` WebSocket frames** **Moderate** - [GHSA-65rj-r9fh-jp2v](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-65rj-r9fh-jp2v)
- **LIVE query subscriptions survive session state changes** **Moderate** - [GHSA-4m82-p8cx-f94j](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-4m82-p8cx-f94j)
- **Field-level SELECT permissions leaked via arithmetic error messages** **Moderate** - [GHSA-6g9v-7gq3-p2c6](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-6g9v-7gq3-p2c6)
- **Field-level SELECT permissions bypassed via JSON Patch `copy` / `move`** **Moderate** - [GHSA-fpxg-5xmv-922m](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-fpxg-5xmv-922m)
- **`RELATE` overwrites existing edge records without `UPDATE` permission** **Moderate** - [GHSA-f82j-v89j-mf86](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-f82j-v89j-mf86)
- **LIVE subscribers can read hidden records via captured `$value` / `$before` / `$after` / `$event`** **Moderate** - [GHSA-6wqw-vhfr-9999](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-6wqw-vhfr-9999)
- **Port-specific `--deny-net` rules bypassed on HTTP redirect** **Moderate** - [GHSA-97vg-427p-8hx5](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-97vg-427p-8hx5)
- **Implicit namespace/database creation via `USE` bypasses DEFINE auth** **Moderate** - [GHSA-wp87-mgvq-5j93](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-wp87-mgvq-5j93)
- **Field-level SELECT permissions bypassed via indexed COUNT fast paths** **Moderate** - [GHSA-c8jx-96c9-8xrp](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-c8jx-96c9-8xrp)
- **Edge `PERMISSIONS FOR delete` bypassed when a connected node is deleted** **Moderate** - [GHSA-whwg-vh4f-pmmf](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-whwg-vh4f-pmmf)
- **`DEFINE ACCESS … ALGORITHM ES512` silently downgraded to ES384** **Moderate** - [GHSA-fwg2-gr34-q3w8](https://github.com/surrealdb/surrealdb/security/advisories/GHSA-fwg2-gr34-q3w8)

## Breaking changes

Catalog and KV on-disk layouts are unchanged from 3.0.x, so you can upgrade in place. The items below are **behaviour or default changes** that can affect clients, dashboards, or SurrealQL that relied on previous (often undocumented) semantics.

### HTTP `/key` request bodies

`POST`, `PUT`, and `PATCH` on [`/key/:table`](https://surrealdb.com/docs/reference/rest-api/http-protocol) and [`/key/:table/:id`](https://surrealdb.com/docs/reference/rest-api/http-protocol) no longer treat the request body as SurrealQL to execute. The body is parsed as a **single SurrealQL value** and bound to **`$data`** only. The value must be **inert**: literals, `$param` references, and constants only. Function calls, idioms, statements, and parenthesised executable forms are rejected.

If you previously sent bodies such as `time::now()`, `fn::…()`, or multi-statement SurrealQL through `/key` expecting server-side evaluation, move that logic to [`POST /sql`](https://surrealdb.com/docs/reference/rest-api/http-protocol) or RPC instead, or compute values in the client and send JSON literals.

#### Unified observability metrics

The community metrics pipeline was reorganised under the `surrealdb.*` scope with a new `/metrics` allow-list. Existing Prometheus dashboards and alerts that scrape metric names from 3.0.x need updating. See the [observability documentation](https://surrealdb.com/docs/manage/observability) for the migration table.

#### RocksDB readahead default

`SURREAL_ROCKSDB_MAX_AUTO_READAHEAD_SIZE` now defaults to **256 KiB** on every deployment instead of RAM-tiered values up to 4 MiB. Override the variable if you tuned scan behaviour around the old defaults.

#### Filesystem bucket key casing

For filesystem-backed buckets, the `lowercase_paths` URL option now defaults to **`false`**. Keys are stored with the casing you supply. Set `?lowercase_paths=true` explicitly if you depend on the previous default of folding paths to lowercase on write.

#### GraphQL Apollo schema naming

The auto-generated GraphQL schema now follows a single Apollo-style naming convention (there is no `NAMING` config). Existing clients must update query and mutation names:

| Before (3.0.x) | After (3.1.0) |
| --- | --- |
| `_get_person(id: …)` | `person(id: …)` |
| `person(filter: …)` (list) | `people(filter: …)` (pluralised) |
| `person_aggregate(…)` | `people_aggregate(…)` |
| `createManyPerson(…)` | `createPeople(…)` |

Regenerate introspection, client stubs, and saved queries after upgrade. Use `GRAPHQL_ALIAS` on [`DEFINE TABLE`](https://surrealdb.com/docs/reference/query-language/statements/define/table), [`DEFINE FIELD`](https://surrealdb.com/docs/reference/query-language/statements/define/field), or [`DEFINE FUNCTION`](https://surrealdb.com/docs/reference/query-language/statements/define/function) when the default pluralisation collides or when camelCase names are required. See **GraphQL** under Improvements for the full surface.

#### GraphQL object and input types

GeoJSON **output** and **input** geometry types now expose `coordinates` as the `JSON` scalar instead of deeply nested `Float` list types. The wire shape at query time is still nested arrays of numbers. Only the introspected GraphQL type changes. Clients that generated queries or validators from the old nested-float schema must be regenerated.

#### `--allow-net` now also gates resolved private and special-use IPs

When a hostname permitted by `--allow-net` resolves to a private or special-use IP, the resolved IP must also be covered by an `--allow-net` entry. Public IPs are unaffected and the check is skipped under `--allow-net all`. To allow internal traffic by hostname, for example `--allow-net internal.example.com` resolving to `10.0.5.42`, add the range: `--allow-net internal.example.com,10.0.5.0/24`.

Ranges checked: IPv4 loopback (`127.0.0.0/8`), RFC 1918 (`10/8`, `172.16/12`, `192.168/16`), link-local (`169.254.0.0/16`), shared (`100.64.0.0/10`), broadcast, and unspecified. For IPv6: loopback (`::1`), unspecified (`::`), unique-local (`fc00::/7`), and link-local (`fe80::/10`). IPv4-mapped IPv6 is canonicalised first.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.0 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released May 14, 2026

## 3.1.0-beta.3

PRE-RELEASE

This is the third beta in the 3.1 series. It adds DiskANN as a second approximate-nearest-neighbour index type alongside HNSW (with an end-to-end overhaul of the ANN warm-lookup path that benefits both implementations), brings the new executor's `WHERE`-clause prefilter to every KV scan with `EXPLAIN` visibility, and lets Surrealism plugins write `async fn` directly. The full cumulative scope from `v3.0.5` is documented in the [v3.1.0-beta.1](https://surrealdb.com/releases/3.1.0-beta.1) entry; the items below describe only what changed between beta.2 and beta.3.

## Highlights

### DiskANN approximate-nearest-neighbour index

A new vector index type sits alongside HNSW under the same KNN query operator, with an overhauled warm-lookup path that benefits both implementations.

- New index syntax:

  ```sql
  DEFINE INDEX pts_embedding_diskann ON pts FIELDS embedding
      DISKANN DIMENSION 4 DIST EUCLIDEAN TYPE F32 DEGREE 16 L_BUILD 64;

  SELECT id, label FROM pts
  WHERE embedding <|2,64|> [0f, 0f, 0f, 0f];
  ```

  `<|K, EF|>` queries DiskANN through the same operator used for HNSW.
- Both DiskANN and HNSW gain `TYPE F16`, `TYPE U8`, and `TYPE I8` element types, plus `DISTANCE INNER_PRODUCT` and `DISTANCE COSINE_NORMALIZED` metrics. Add `HASHED_VECTOR` to the index definition if vector-to-document mappings should be stored through vector hashes instead of full vectors.
- The RocksDB-backed ANN lookup path was reworked end-to-end. The dominant cost on warm-lookup profiles - scanning empty pending-update ranges - is gone; in its place sit process-local caches for graph payloads, vector-to-document doc sets, and doc-id-to-record-id mappings. KV stays the source of truth: cache misses fall back to existing reads, write paths evict, and uncommitted writes never poison the positive doc-id cache.
- Lookup result materialisation is batched, and graph candidates resolve by `ElementId` first so warm lookups skip the previous re-read of candidate vectors.
- `DEFINE INDEX` on an ANN index now drains pending vectors before returning, so a freshly-defined index is immediately usable.
- The pending-state skip is distributed-safe: new `!dp` (DiskANN) and `!ht` (HNSW) guard keys are written conditionally by compaction, so on multi-node deployments lookups only skip pending scans when KV state confirms the range is empty.

### Predicate prefilter for KV scans

The new executor's KV scan operator evaluates supported `WHERE` predicates against the raw RocksDB key/value pair before the record is decoded. Rows whose predicate is provably false from raw bytes are rejected before revision decode, schema enforcement, projection, and downstream operators run.

- Supported predicate shapes: root-level equality (`a = …`), `IN` / `NOT IN` against literal arrays, byte-level `CONTAINS` / `CONTAINSANY` / `CONTAINSALL`, simple unary `NOT`, conjunctions/disjunctions of the above, and nested shared-prefix accesses such as `outer.x AND outer.y`.
- Previously gated behind the `SURREAL_PREDICATE_PREFILTER` environment variable; now always on, with the legacy parallel path removed.
- `EXPLAIN` reports whether the prefilter applied and which predicates were pushed down, so operators can confirm the optimisation is firing.
- Applies on top of every scan operator that touches RocksDB - base table, indexed, unique compound, and full-text - without configuration.

### Async functions in Surrealism plugins

Plugin authors can mark functions exported through `#[surrealism]` as `async fn` and `.await` host imports directly (storage, network, queries).

- Modules can now use async crates like `reqwest` and `sqlx` that internally depend on `tokio::spawn` for connection pooling and timers.
- The host runtime is initialised once per module instance and persists across invocations on pooled controllers, so there's no per-call setup cost.
- The accompanying `surrealism::demo` crate gains an `async fn fetch_pokemon` example that exercises WASI socket access under `allow_net = ["127.0.0.1"]`.
- This is a stopgap until WASM Preview 3 / component-model async lands upstream, at which point the embedded executor becomes unnecessary.

## Improvements

- **Durable, cluster-wide index build coordination.** Concurrently built indexes now keep their build state in the catalog rather than in process memory, and every node coordinates through it. On multi-node deployments sharing a storage engine, all nodes now agree on which node owns the build, which writes the in-progress index admits, when replay is complete, and when the index becomes query-eligible. `INFO FOR INDEX` reflects the durable state. The same change closes a class of issues where COUNT indexes could overcount when the initial build ran on multiple nodes concurrently. No SurrealQL surface change.
- **Lower default RocksDB readahead for better NVMe scan throughput.** The default `SURREAL_ROCKSDB_MAX_AUTO_READAHEAD_SIZE` is now 256 KiB on every deployment, replacing the previous RAM-tiered defaults that reached 4 MiB. Most NVMe block layers cap a single device IO around `/sys/block/<device>/queue/max_sectors_kb` (typically 128–512 KiB), so larger readahead values were being split into multiple IOs with per-IO overhead and over-prefetching. On a representative NVMe seek-random benchmark the per-machine optimum was 128 KiB at 578 ops/sec / 640 MB/s; 4 MiB readahead was ~60% slower. Operators with non-standard storage can still override via the environment variable.
- **Optional RocksDB scan checksum verification.** New `SURREAL_ROCKSDB_SCAN_VERIFY_CHECKSUMS` flag, default `true` (existing behaviour). Setting it to `false` skips CRC32C verification on first-read blocks during scans and counts; cached blocks were never re-checksummed either way. Trades a small amount of integrity checking for cold-scan throughput on trusted storage.
- **SurrealKV updated to 0.21.2** picking up the upstream fixes from [surrealdb/surrealdb#7303](https://github.com/surrealdb/surrealdb/issues/7303).

## Bug fixes

- `[View]` Grouped view writes now emit changefeed deltas and trigger LIVE-query notifications. Previously, writes to a `DEFINE TABLE … AS SELECT … GROUP BY …` view skipped the changefeed-emit and live-query-notify hooks because the aggregate metadata is written directly through `tx.set_record` - so `SHOW CHANGES` and live-query subscribers missed updates to grouped views. The view-write lifecycle now matches the regular document pipeline.
- `[Query Engine]` `NONE.*`, `NULL.*`, scalar `.*`, and geometry `.*` now return `NONE` in the new streaming planner - matching the legacy executor - instead of wrapping the value in a single-element array. Fixes the Go SDK `cannot decode array into <StructType>` error that appeared when an `option<record<…>>` field was `NONE` and the query expanded it with `.*`. Mixed arrays also now pass non-record elements through unchanged, so `[1, record:r, "x"].*` returns `[1, {fetched}, "x"]` rather than `[[1], {fetched}, ["x"]]` (fixes [surrealdb/surrealdb#7143](https://github.com/surrealdb/surrealdb/issues/7143)).
- `[Functions]` `value::diff` followed by `value::patch` now round-trips correctly on top-level scalars. `value::diff` of two scalars produced a JSON Patch with the empty pointer `""` as its path; the patch parser was treating that as a single-empty-segment path rather than the document root, so `value::patch` silently no-op'd and returned the original value (fixes [surrealdb/surrealdb#7239](https://github.com/surrealdb/surrealdb/issues/7239)).
- `[Functions]` Empty-group sentinels for aggregate functions are now consistent with the corresponding scalar functions:
  - `time::min` and `time::max` return `NONE` for empty groups (previously returned the chrono `MAX_UTC` / `MIN_UTC` placeholders, which surfaced to clients as `d'+262142-12-31T23:59:59.999999999Z'`).
  - `math::variance` and `math::stddev` return `NaN` for empty groups (previously returned `0.0`, which conflated "no data" with "all values identical"). A single-element group still returns `0.0`.
  - Fixes [surrealdb/surrealdb#7301](https://github.com/surrealdb/surrealdb/issues/7301).
- `[Functions]` Materialised aggregate views now compute `math::pow` against the `sum` accumulator (previously used `count`, returning the wrong magnitude) and `time::min` via the correct `DatetimeMin` aggregate (previously used `DatetimeMax`, returning the maximum).
- `[Indexes]` Catalog ID sequences now seed safely on the first boot after upgrade. The new per-node batch-based ID generator initialised at `0` when its state key was missing, so the first namespace, database, table, or index created after an upgrade could reuse an existing ID and collide with the live catalog entry. The sequence loader now scans the existing catalog and starts from `max(existing_id) + 1` whenever the state key isn't present.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.0-beta.3 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released May 8, 2026

## 3.1.0-beta.2

PRE-RELEASE

This is the second beta in the 3.1 series and continues the stabilisation focus, with a new audit-logging and slow-query pipeline in the Enterprise product. The full cumulative scope from `v3.0.5` is documented in the [v3.1.0-beta.1](https://surrealdb.com/releases/3.1.0-beta.1) entry; the items below describe only what changed between beta.1 and beta.2.

Items tagged `(Enterprise)` on a section heading or `[Enterprise]` on a bullet refer to the SurrealDB Enterprise product. Everything else applies to community.

## Highlights

### Audit logging and slow-query telemetry (Enterprise)

A new audit-logging pipeline with configurable sinks, redaction, and slow-query detection has been added to Enterprise.

- A queued, worker-backed `audit_log` module records authenticated operations to a configurable sink, with a redaction layer that strips sensitive fields before serialisation
- Slow-query detection runs alongside the audit pipeline and emits structured records for queries that exceed the configured threshold
- Configuration is exposed through new `SURREAL_AUDIT_*` and `SURREAL_SLOW_QUERY_*` environment variables (see `crates/enterprise/src/cnf/audit.rs` and `crates/enterprise/src/cnf/slow_query.rs`)
- The audit queue, observer, record, redact, sink, and worker modules sit under `crates/enterprise/src/observe/audit_log/`

## Improvements

- Extended the community OpenTelemetry pipeline with MCP request instrumentation, error classification, and additional protocol-level metrics (#67)
  - A new `observe/error_class.rs` module categorises errors emitted by the executor, KV transactions, and the RPC and WebSocket layers, so dashboards can distinguish client errors from server errors without parsing message strings
  - The MCP server now emits per-tool request metrics alongside the GraphQL and HTTP/WebSocket surfaces via a new `observe/mcp_adapter.rs`
  - GraphQL, HTTP-tower, and WebSocket instrumentation surfaces are widened to record more of the request lifecycle
- Bounded shutdown background work to mirror the bounded-startup retry behaviour introduced in beta.1 (#41)
  - Shutdown of long-running background tasks now uses `tokio::time::timeout_at` against the global deadline, so a stuck task can no longer indefinitely delay datastore shutdown
  - Added `is_query_cancelled` and `is_query_timedout` helpers in `err/mod.rs` so the shutdown path can distinguish expected cancellation from real errors
- Enabled TLS for the OTLP exporters and aligned `rpc.request_id` with the OpenTelemetry semantic conventions (#53)
  - The OTLP push exporter now builds with the `tls` and `tls-roots` features and uses the platform's native root store via `rustls-native-certs`
  - `rpc.request_id` is emitted as a string identifier rather than the previous binary form, matching the OTel `rpc.*` conventions used by external collectors
  - The OTLP runtime is now driven by an async runtime so exporters no longer block on a dedicated thread
- Removed `LazyLock` from the hot path to eliminate a small but measurable per-request synchronisation cost (#48)
- Avoided emitting empty reference range deletes during the reference purge so the storage layer no longer pays for a no-op range delete on tables with no references (#49)
- Upgraded the Rust toolchain to 1.95 across community (#57) and `[Enterprise]` (#81).

## Bug fixes

- `[Query Engine]` Fixed coercion error messages omitting the position of the offending element, which made `expected array<int>` / `expected tuple` errors unactionable on large inputs. Coerce and cast paths in `val/value/convert/{coerce,cast}.rs` now thread the element index through the error (#52, resolves #5677).
- `[Indexes]` Fixed transient transaction conflicts at commit time dropping in-flight background index work. The background index runner now distinguishes recoverable conflicts from fatal errors and retries against fresh state, with the retry harness shared across `kvs/index.rs` and `kvs/ds.rs` (#69).
- `[Indexes]` Fixed `VecMap::append` extending the map in a way that could leave duplicate keys behind. The `surrealdb-collections` crate's `VecMap` now de-duplicates and re-sorts on append (#59).

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.0-beta.2 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Released May 1, 2026

## 3.1.0-beta.1

PRE-RELEASE

This is the first beta in the 3.1 series. The focus is bug fixing and stabilisation of the 3.0 line, with a number of substantial additions on top of the fixes that already shipped in `v3.0.1`–`v3.0.5`.

## Highlights

### Bug fixes and stabilisation

This release fixes over 40 community-reported bugs.

### A first-party Model Context Protocol (MCP) server

Exposed as a `surreal mcp` stdio subcommand for local IDE integrations, and as an HTTP `/mcp` endpoint guarded by the existing authentication middleware.

- The tool surface includes typed `query`, `select`, `create`, `insert`, `upsert`, `update`, `delete`, `relate`, `info`, `list`, `use`, and `run` tools
- `read_only_hint` / `destructive_hint` annotations so MCP clients can prompt the user before mutating operations
- Request bodies are bound as typed `Variables` rather than string-interpolated
- Identifiers are validated against a strict allow-list
- Per-request subject verification rejects cross-session credential reuse
- New environment variables: `SURREAL_HTTP_MAX_MCP_BODY_SIZE` (default 4 MiB), `SURREAL_MCP_QUERY_TIMEOUT_SECS` (default 60 s), `SURREAL_MCP_MAX_RESULT_BYTES` (default 256 KiB), `SURREAL_MCP_RUN_MAX_ARGS` (default 64), and `SURREAL_MCP_PARAMS_MAX_KEYS` (default 256)

### A unified observability and monitoring pipeline built on OpenTelemetry

Overhauled the observability and monitoring layer onto a single OpenTelemetry pipeline.

- The previous community Prometheus path and the parallel `OtelObserver` SemConv pipeline have been collapsed onto one `SdkMeterProvider` and one `SdkLoggerProvider`
- Every signal is recorded once and routed to many exporters: Prometheus text on `/metrics`, OTLP metrics push, OTLP logs push
- Metric families are reorganised under the `surrealdb.*` scope (statement, query, transaction, RPC, auth, session, network, HTTP, live query, process, storage)
- The `/metrics` endpoint enforces a render-time `PUBLIC_METRICS` allow-list: anonymous scrapers see only aggregate process gauges, while root-authenticated operators see the full surface
- Tenant context (`namespace` / `database` / `user`) is carried on every labelled family but filtered out of the public view by family name
- New environment variable: `SURREAL_PROCESS_METRICS_REFRESH_INTERVAL` (default 5 s)
- Retired: `SURREAL_TELEMETRY_NAMESPACE`, `SURREAL_TELEMETRY_RPC_LIVE_ID`
- Repurposed: `SURREAL_METRICS_ENABLED`
- This is a breaking change for existing dashboards; see `doc/OBSERVABILITY.md` for the migration table

### Memory optimisations on the value layer

A round of memory work on the value layer including a 24-byte small-string-optimised `Strand` type, a new `surrealdb-collections` crate with `VecMap`/`VecSet`, and a per-transaction overhead reduction on the hot path.

- Introduced `Strand`, a 24-byte small-string-optimised immutable string type, routed through every core hot-path string: `Value::String`, `Object` keys, `TableName`, and `RecordIdKey::String`
- `Strand` packs into the same 24 bytes as `String` via a `repr(C)` SmolStr-style tagged union, with an inline variant storing strings up to 23 bytes with zero heap allocation
- A heap variant uses `Arc<str>` so clones are a refcount bump rather than a copy
- Object entries and `Value::String` shrink by 8 bytes each, which compounds at document scale
- The wire format is unchanged: `Strand` serialises identically to `String` through `serde`, `Revisioned`, and `storekey`
- A companion `Strand::new_static` constructor lets compile-time-known strings in core skip allocation entirely

### Significant RocksDB tuning across the engine

Substantial performance improvements through engine tuning including RocksDB 11.0.0 upgrade, prefix extractor, blob-file separation by default, and scaled defaults for constrained environments.

- RocksDB upgraded to 11.0.0 with enabled prefix extractor for keyspace-aware bloom filters
- Aligned the compaction readahead size to `max_sector_kb` on disk (currently 256 KiB on Cloud)
- Enabled blob-file separation by default for large-value workloads
- Scaled defaults for constrained environments (the SurrealDB Cloud free tier sees disproportionate memory and disk pressure on the previous mid-sized defaults)
- Large deployments (≥16 GiB memory) keep the previous defaults via tiered scaling; small deployments get appropriately sized buffers, caches, and compaction settings out of the box

### Expanded `ALTER` coverage

Every `DEFINE` statement now has a matching `ALTER` counterpart.

- Added `ALTER` support for `EVENT`, `PARAM`, `BUCKET`, `ANALYZER`, `FUNCTION`, `USER`, `ACCESS`, `CONFIG`, and `API`
- Each follows the same property-by-property pattern as the existing `ALTER TABLE` / `FIELD` / `INDEX` statements
- Individual properties on a definition can be modified without re-specifying the whole definition via `DEFINE … OVERWRITE`
- `ALTER PARAM` does not support `DROP VALUE` (a param without a value is meaningless)
- `ALTER EVENT` uses `DROP ASYNC` to revert to synchronous mode
- `ALTER ACCESS` does not allow changing the access type itself (record / JWT / bearer), only durations, the `AUTHENTICATE` clause, and comment

## Improvements

- Added `REMOVE CONFIG [IF EXISTS] GRAPHQL | API | DEFAULT` to mirror `DEFINE CONFIG`, providing a way to delete `GRAPHQL`, `API`, and `DEFAULT` configurations.
- Extended GraphQL schema generation to support fields whose type is a literal (record / object / array literal types), so they now appear in the generated GraphQL schema rather than being silently omitted.
- Added the `surrealdb-collections` workspace crate with `VecMap` and `VecSet`
  - Vec-backed, ordered, insertion-order-preserving map and set types
  - Tuned for the small-collection workloads that dominate SurrealDB's value layer
  - `Object` and `Set` in `surrealdb-core` now use these types
  - Linear scan for small slices, skipped re-sort when inputs are already ordered, and a tuned linear-vs-binary search threshold
  - User-facing semantics are unchanged
- Upgraded RocksDB to 11.0.0 and tuned the engine
  - Enabled prefix extractor for keyspace-aware bloom filters
  - Aligned the compaction readahead size to `max_sector_kb` on disk (currently 256 KiB on Cloud)
  - Enabled blob-file separation by default for large-value workloads
  - Scaled defaults for constrained environments (the SurrealDB Cloud free tier sees disproportionate memory and disk pressure on the previous mid-sized defaults)
  - Large deployments (≥16 GiB memory) keep the previous defaults via tiered scaling; small deployments get appropriately sized buffers, caches, and compaction settings out of the box
- Reduced per-transaction overhead on the hot path
  - `FunctionRegistry::with_builtins` is now built once per datastore and shared across transactions instead of being rebuilt on every query (was ~22% of CPU on a CREATE-heavy profile)
  - The change-feed `Writer` is allocated lazily so transactions that touch no changefeed-enabled tables no longer pay for a `DashMap<ChangeKey, TableMutations>` and its drop
  - The per-transaction `quick_cache` now uses a single shard sized for transaction-scope rather than the `available_parallelism() * 4` default, eliminating the 256 `CacheShard` allocations that used to happen on every `Transaction::new` on a 64-core host
- Updated SurrealKV to 0.21.1 and raised the maximum memtable size for large transactions, allowing larger batches to be inserted without flushing mid-transaction.
- Tuned RocksDB scans and grouped-commit
  - Read-only transactions now bypass the `BaseDeltaIterator` merge layer in `count`, `keys`, `keysr`, `scan`, and `scanr`, which removes overhead on every step
  - The offload heuristic for moving large scans to the storage threadpool now uses byte-based sizing (including skip work) and handles `BytesOrCount` correctly
  - The grouped-commit coordinators (RocksDB and SurrealKV) had shutdown and lost-wakeup edge cases that could stall threads or callers; both are fixed
- Capped the scanner's initial batch size using the query `LIMIT` so that small-`LIMIT` queries don't fetch more rows than they will ever return.
- Added a sync fast-path for `PhysicalExpr` evaluation in scan filters, avoiding the async overhead for the common case where no IO is required.
- Avoided cloning the document on `SELECT *` projection where the source row can be passed through unchanged, reducing allocator pressure on read-heavy workloads.
- Replaced the global HTTP client cache (which keyed on a hash of capabilities) with a single `HttpClient` instance per `Datastore`, exposed via `Context::http_client`
  - Removes process-wide global state
  - Simplifies test setup
  - Removes per-request hashing
  - `Context::background` is renamed to `Context::new_test` and is now test-only
  - `Context::new` is renamed to `Context::new_child` to make its sub-context role explicit
- Made datastore initialisation retry resilient to hung attempts
  - Each retry attempt is now wrapped in its own per-attempt `tokio::time::timeout` that increases linearly (10 s, 20 s, 30 s, …)
  - A single hung attempt against a slow or unresponsive storage backend can no longer silently consume the entire retry budget
  - Per-attempt timeouts and back-off sleeps are capped to the remaining global budget
  - The global retry budget is raised from 60 s to 120 s
  - Failures are now logged with a per-task label so the failed operation is identifiable
  - Exhaustion returns a descriptive `Internal` error instead of re-raising the last `TransactionConflict`
- Added RocksDB user-defined timestamps and threaded the `VERSION` clause through every internal schema lookup
  - Versioned reads (`SELECT … VERSION d'…'`, `INFO FOR DB / TABLE VERSION …`) are now isolated from the LRU transaction cache so historical reads cannot pollute current-time reads
  - The `VERSION` clause is now propagated through graph and reference traversals, the `FETCH` clause, automatic record dereferences during field access, and inherited by inner subqueries when the outer `SELECT` carries a version
  - `INFO FOR ROOT` and `INFO FOR NS` now also accept `VERSION`
  - RocksDB gains a custom comparator that stores 8-byte LE timestamps as key suffixes (newer versions ordered before older), with HLC commit timestamps assigned at commit time and `ReadOptions::set_timestamp` for point-in-time reads
- Overhauled the Surrealism (WASM plugin) stack to v0.2.0 and migrated to WASI Preview 2 (Component Model)
  - Modules now target `wasm32-wasip2` and a WIT interface (`surrealism/wit/surrealism.wit`) defines the host/guest contract
  - New `#[surrealism]` attribute options: `writeable` (lets the planner choose appropriate transaction modes), `comment` (description visible in `surreal module info` and `INFO FOR DB STRUCTURE`), `init` (one-time init hook with access to host imports), and namespaced module exports that produce prefixed names like `math::add`
  - Data crossing the host/guest boundary now serialises via FlatBuffers
  - Modules can declare an attached read-only filesystem (`[attach] fs = "fs"` in `surrealism.toml`) bundled into the `.surli` archive and mounted at `/` inside the WASM sandbox
  - The host runtime gets a two-engine epoch model - a guarded engine with epoch interruption for timeout enforcement, and a fast engine for trusted compute-heavy modules - selected via `strict_timeout` in `surrealism.toml`
- Added a builder pattern to `Datastore` for properties that cannot be changed after construction, replacing the previous post-construction setters
  - Makes invalid configurations harder to express
  - Lets the language-test suite exercise more configuration surfaces
  - The language-test harness has been overhauled in the same change
- Expanded the `surrealdb-types` SDK and derive surface
  - Added `#[surreal(rename_all = "...")]` on enums and structs and honoured per-variant `#[surreal(rename = "...")]` (previously parsed only for fields)
  - Added `#[surreal(wrap)]` for transparent wrapper types
  - Added more `SurrealValue` implementations for standard-library types and a `NaiveDate` implementation
  - Generalised the `Cow` and `&str` `SurrealValue` implementations
  - Added serde interop on the `Value` type
  - Restored query chaining on the `Query` builder
  - Added several missing crate exports
- Auto-detect TTY for the console log output and respect the `NO_COLOR` environment variable
  - ANSI escape codes are now only emitted when both stdout and stderr are TTYs
  - Fixes garbled output in Docker containers, Kubernetes pods, and log shippers like Kibana and GKE Log Explorer
- Allow `surreal validate` to read input from stdin, so SurrealQL can be piped in for syntax checking without writing to a file first.
- Removed several process-wide configuration globals in `surrealdb-core` so configuration is now carried through `Datastore` / `Context` rather than read from static state, simplifying embedding and testing.

## Bug fixes

- `[Query Engine]` Fixed `$parent` resolving incorrectly inside graph `WHERE` clauses in nested subqueries; `$parent` now refers to the current `SELECT`'s row rather than an outer subquery's parent.
- `[Query Engine]` Fixed `FROM ONLY` not being propagated through fused graph lookup chains, which previously produced incorrect results from constrained graph traversals.
- `[Query Engine]` Fixed `ORDER BY` (and `WHERE`) being silently ignored in subqueries with graph traversal sources like `FROM $parent->edge->target`. The streaming source operator now resolves `RecordId` values to full documents so downstream `Filter`, `Sort`, `Group`, and `Split` operators have the fields they need.
- `[Query Engine]` Fixed bare field paths in a subquery's `FROM` clause (e.g. `FROM data.files` where `data` is a record link on the outer document) evaluating to `NONE` because the source operator had no current value in correlated-subquery context.
- `[Query Engine]` Fixed `$parent` not being bound during `iterator.prepare`, which caused correlated subqueries using `FROM $parent->…` (including nested `CREATE`, `UPDATE`, and `DELETE`) to see `$parent` unset while select targets were being planned.
- `[Query Engine]` Fixed `$parent` resolution in materialised view definitions on the streaming path so view materialisation now sees the same parent context as the legacy executor.
- `[Query Engine]` Fixed `LET` bindings in planned bodies not being visible to `IF` / `FOR` blocks that fall back to the legacy compute path; the legacy `FrozenContext` is now refreshed after a planned `LET` body runs.
- `[Query Engine]` Fixed `$auth` and `$session` returning `NONE` in queries executed inside a client-side transaction; the session is now correctly attached to the per-statement context.
- `[Query Engine]` Fixed duplicate constant values being omitted in the streaming executor (e.g. `false AS isLiked, false AS isLocked` collapsing to a single column). The expression registry's de-duplication key now includes the alias.
- `[Query Engine]` Fixed nested `.*` destructure returning `NONE` for record links; `DestructureField::All` now fetches the linked record before expanding.
- `[Query Engine]` Fixed `SELECT` alias shadowing the source field of the same name on the projection fast path.
- `[Query Engine]` Preserved alias idiom structure in streaming-executor projections so that `AS foo.bar` continues to nest into `{ foo: { bar: _ } }` while `` AS `foo.bar` `` stays as a flat key - both the executor projection and `GROUP BY` alias paths previously stringified the alias and lost the backtick distinction.
- `[Query Engine]` Fixed `??` (null coalescing) and `?:` (ternary condition) operator precedence binding tighter than arithmetic. `$ctx.limit ?? 2 ** 31 - 1` now parses as `$ctx.limit ?? (2 ** 31 - 1)`, matching JavaScript / C# / Kotlin / Swift / PHP semantics; previously the operators bound above `**`, `*`, `/`, `+`, and `-`, which made `??` nearly useless without explicit parentheses.
- `[Query Engine]` Fixed `value::diff` and `value::patch` returning `Function 'value::…' requires async execution` when invoked via method syntax (`{a: 1}.diff({a: 2})`). The async-vs-sync check now matches the behaviour of the full-path call.
- `[Query Engine]` Fixed view materialisation crashing when the source `SELECT` aliased the `id` field; initialisation now falls back to scanning for any `RecordId` when the `"id"` key is absent.
- `[Query Engine]` Fixed references being created against a target table even when the originating `CREATE`/`INSERT`/`RELATE`/`UPSERT`/`UPDATE` failed its `CREATE` permission check; references are now cleaned up on the permission-denied error path.
- `[Query Planner]` Fixed `WHERE` filters on indexed fields silently omitting `COMPUTED` fields from evaluation. `IndexScan`, `FullTextScan`, and `KnnScan` now resolve field state and process computed fields through their pipelines, matching `TableScan` and `UnionIndexScan`.
- `[Query Planner]` Fixed records with `NONE` / `NULL` fields being silently excluded from `ORDER BY` results when the sort key was on a unique compound index; unique indexes now store `NONE` / `NULL` tuples in the non-unique key format so they remain visible to scans (`REBUILD INDEX` is required to fix existing indexes).
- `[Query Planner]` Fixed `SELECT VALUE` with `ORDER BY` ignoring the sort order when querying from an array source; the `VALUE` projection is now applied after sorting and pagination instead of before. `ORDER BY` can also reference a `SELECT VALUE` alias.
- `[Query Planner]` Fixed `ORDER BY DESC` with a variable `LIMIT` inside nested `IF` / `LET` blocks inside a function producing wrong results.
- `[Query Planner]` Fixed `ORDER BY` on edge-table queries with record-link source fields like `in.creationDate` returning unsorted output. The sort path now routes resolved alias expressions through the `Compute` operator (which can fetch linked records) and restricts the synchronous `FieldPath` shortcut to single-part paths where record-link traversal is impossible.
- `[Query Planner]` Fixed `KNN <|K, DISTANCE|>` queries bypassing the HNSW index; `eval_hnsw_knn` now matches the `K(k, d)` variant when the distance matches the index configuration.
- `[Indexes]` Made full-text, COUNT and HNSW compaction SSI-safe so concurrent index activity no longer drops work that was committed after the compaction snapshot. Compaction is now split-phase and generation-guarded, with conditional writes that advance an internal generation key and delete only the exact delta keys captured in the read phase. Full-text and COUNT compaction plans are bounded to `COUNT_BATCH_SIZE` (50,000) delta keys per batch, so a long-stalled delta backlog is processed in fixed-size batches instead of being held entirely in memory. HNSW pending updates now use record-keyed entries so sequential writes from multiple SurrealDB nodes against the same remote backend no longer depend on local append-key ordering.
- `[Indexes]` Fixed concurrent index conflicts and reduced overhead during full-text concurrent builds. The initial-build write transaction no longer reads `!ip` / `!ig` queue handoff records (those are now read through a separate read-only transaction, so the initial-build write transaction stays out of active queue contention). `maybe_consume` no longer writes `!ip` keys after the initial build phase. The append-phase commit retries on transient transaction conflicts instead of treating them as fatal. Full-text concurrent builds now reuse `FullTextIndex` across documents in a batch instead of recreating it per document.
- `[Indexes]` Fixed cascade-delete index bugs that caused COUNT-index drift, ghost UNIQUE-index entries on the relation table after deleting a vertex with edges, and phantom UNIQUE-index entries when `IN` / `OUT` records were deleted before the relation. Also fixed `process_lookup` hardcoding `None` for the version parameter, which caused `SELECT … WITH VERSION` queries that traverse graph edges to silently return current-state records instead of the requested historical snapshot.
- `[Live Queries]` Terminate live queries on session invalidation and TTL expiry. `invalidate()` now calls `cleanup_lqs()`, mirroring `reset()`, so all live-query registrations for the session are removed from the notification engine. The dispatcher also compares the session expiry timestamp against the current time before sending each notification and silently skips expired ones, closing a class of bug where revoked sessions continued receiving CREATE / UPDATE / DELETE events indefinitely.
- `[Live Queries]` Prevent ignorable evaluation errors in a `LIVE SELECT`'s `WHERE` clause or `SELECT` projection (type mismatches, invalid function arguments) from propagating back through `try_join_all` and aborting the enclosing write transaction. Such errors now silently skip the notification.
- `[Storage]` Fixed long server non-responsiveness when a client cancels `surreal export` mid-stream. The HTTP export handler now exits the proxy task when the body stream channel fails (indicating client disconnect), which causes the next channel send to fail and aborts the export task - releasing the read transaction promptly. On large databases this previously blocked endpoints like `/health` while the cancelled export ran to completion.
- `[Indexes/SurrealKV]` Allow large transactions to insert without flushing mid-transaction by raising the maximum memtable size for SurrealKV.
- `[RPC/WebSocket]` Respond with a failure when an in-flight WebSocket request is cancelled rather than silently dropping it, so clients always observe an explicit response.
- `[Transactions]` Surface unified `Cannot COMMIT: …` errors when an explicit `COMMIT` after a previous statement abort fails (e.g. unique constraint), instead of returning only the first statement error. `txn.commit()` failures and the post-abort `COMMIT` fast-forward now share the same error wording so clients no longer treat a missing row as success.
- `[Auth]` Fixed `id` resolving to `NONE` inside a table's `FOR select WHERE … permission` clause when a record was created and selected in the same statement (`SELECT * FROM ONLY (CREATE ONLY a)`), which previously caused the record to be filtered out by its own `SELECT` permission. `RecordId::select_document` now re-injects the `id` into the returned object when it's missing on a transaction-cache hit.
- `[Auth]` Fixed user-defined function `PERMISSIONS` clauses being evaluated for system-level users (`root`, namespace, database). `PERMISSIONS` are intended only for record-level users; system-level users with `Action::View` now correctly bypass the check and can invoke functions defined with `PERMISSIONS NONE`. Also removed a hardcoded `fn::` prefix from the `FunctionPermissions` error message that produced double-prefixed names like `fn::fn::my_function`.
- `[Functions]` Added `encoding::json::encode` and `encoding::json::decode` for serialising SurrealDB values to / from JSON strings, and added support for UTF-16 surrogate-pair escape sequences (`\uD800`–`\uDFFF`) in the JSON parser. The surrogate-pair support is gated on a `json_string_escapes` parser setting so SurrealQL string parsing is unaffected.
- `[Functions]` Use `serde_json` to parse external JSON in `http::*` functions so valid JSON responses containing escaped forward slashes (`\/`) - produced by default `json_encode()` in PHP and other systems - are no longer rejected by SurrealQL's own JSON parser.
- `[Functions]` Fixed `infinity` output and added a non-abbreviated constant path so that the constant is preserved on round-trip.
- `[Functions]` Fixed user-duration structure so that durations defined and stored on user definitions round-trip correctly.
- `[Types/SDK]` Fixed stack overflow on recursive `SurrealValue` types so deeply nested or self-referential type definitions no longer crash on encode / decode.
- `[Types/SDK]` Fixed `SurrealValue` derive macro emitting `r#type` instead of `type` for raw-identifier struct fields. Fields like `r#type: String` now serialise as `"type"` without needing an explicit `#[surreal(rename = "type")]` attribute.
- `[Types/SDK]` Fixed a copy-paste bug in `prepare_array` (in `iterator.rs`) that checked `x[0]` for `Part::Lookup` after already matching it as `Part::Start`, making the lookup-optimisation branch dead code. All edge traversals inside array `FROM` clauses (`SELECT * FROM [a:1->edge->table]`) previously fell through to the slower `prepare_computed` path; the optimised `prepare_lookup` path is now reached.
- `[Types/SDK]` Fixed JSON-patch `remove` operation on lists.
- `[Types/SDK]` Fixed `CREATE` failing for fields typed as `set<…>`.
- `[Imports/Exports]` Fixed re-importing an exported database failing with "field already exists" errors. Export now emits `DEFINE FIELD OVERWRITE` so a clean re-import succeeds without manual intervention.
- `[Logging]` Fixed JSON log format being unused even when configured.
- `[Logging]` Fixed ANSI escape codes leaking into JSON log output (which previously produced corrupt records like `"level": "\x1b[32mINFO\x1b[0m"`) and added per-stream TTY detection so mixed-redirect scenarios like `surreal start > app.log` no longer strip ANSI from the stream that is still an interactive terminal.
- `[CLI]` Prevent panic on systems with restricted `/proc` access (e.g. NixOS with `ProcSubset=pid` hardening) where `sysinfo` cannot read `/proc/meminfo` and `System::total_memory()` returns 0. The previous `cgroup_limits()` assertion put the systemd service into an infinite restart loop on both RocksDB and SurrealKV backends.
- `[Events]` Fixed events defined on a vertex table incorrectly firing for related edge-table records during cascade deletion. When deleting a vertex with graph edges (via `RELATE`), the edge deletes now run with the edge table's document context so the vertex table's events, views, live queries, changefeeds, and field validation are no longer applied to edge-record mutations.

Newer patch available

## Upgrade to 3.1.6

You are viewing the 3.1.0-beta.1 changelog. A newer patch in this release line is available - we recommend running 3.1.6 for the latest fixes and improvements.

[View 3.1.6 release notes](https://surrealdb.com/releases/3.1#release-3-1-6)

Newer release line

### [3.2](https://surrealdb.com/releases/3.2)

Updated Oct 5, 2026

Older release line

### [3.0](https://surrealdb.com/releases/3.0)

Updated Mar 27, 2026

[All releases](https://surrealdb.com/releases)

GET STARTED

## Start building with SurrealDB. One data layer for AI, from object storage to agent memory.

[Start free with SurrealDB](https://studio.surrealdb.com/current/instances/deploy) [Learn more](https://surrealdb.com/docs/what-is-surrealdb)

![ISO 27001](https://surrealdb.com/assets/static/iso.BzQy4FFy.svg)

![AICPA SOC 2 Type 2](https://surrealdb.com/assets/static/aicpa-soc.CE_YjqhK.svg)

![Cyber Essentials](https://surrealdb.com/assets/static/cyber-essentials.CtbmbYfL.svg)

![GDPR](https://surrealdb.com/assets/static/gdpr.IadR7loS.svg)

![Cyber Essentials Plus](https://surrealdb.com/assets/static/cyber-essentials-plus.Cl5P05xe.svg)

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://surrealdb.com"},{"@type":"ListItem","position":2,"name":"Releases","item":"https://surrealdb.com/releases"},{"@type":"ListItem","position":3,"name":"3.1","item":"https://surrealdb.com/releases/3.1"}]}
```

```json
{"@context":"https://schema.org","@type":"TechArticle","headline":"SurrealDB 3.1 release notes","description":"SurrealDB 3.1.6 is a reliability rollup on the 3.1 line, backporting fixes that have already landed on the 3.2 and 3.3 lines. It fixes a batch of concurrent…","datePublished":"2026-09-01T00:00:00.000Z","dateModified":"2026-09-01T00:00:00.000Z","url":"https://surrealdb.com/releases/3.1","mainEntityOfPage":{"@type":"WebPage","@id":"https://surrealdb.com/releases/3.1"},"author":{"@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com"},"publisher":{"@type":"Organization","name":"SurrealDB","logo":{"@type":"ImageObject","url":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg"}},"about":{"@type":"SoftwareApplication","name":"SurrealDB","applicationCategory":"DeveloperApplication","operatingSystem":"Linux, macOS, Windows","softwareVersion":"3.1"}}
```

```json
{"@context":"https://schema.org","@type":"Organization","@id":"https://surrealdb.com/#organization","name":"SurrealDB","url":"https://surrealdb.com","logo":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg","description":"SurrealDB is the context and memory layer for AI agents. A multi-model database for documents, graphs, vectors, and time-series.","foundingDate":"2022","legalName":"SurrealDB Ltd","identifier":{"@type":"PropertyValue","propertyID":"GB-COH","value":"13615201"},"address":{"@type":"PostalAddress","streetAddress":"3rd Floor, 1 Ashley Road","addressLocality":"Altrincham","addressRegion":"Cheshire","postalCode":"WA14 2DT","addressCountry":"GB"},"contactPoint":[{"@type":"ContactPoint","contactType":"customer support","email":"support@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"sales","email":"info@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"security","email":"security@surrealdb.com","url":"https://surrealdb.com/.well-known/security.txt","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"legal","email":"legal@surrealdb.com","url":"https://surrealdb.com/legal","availableLanguage":"English"}],"hasCertification":[{"@type":"Certification","name":"SOC 2 Type 2"},{"@type":"Certification","name":"GDPR"},{"@type":"Certification","name":"Cyber Essentials Plus"},{"@type":"Certification","name":"ISO 27001"}],"owns":[{"@type":"SoftwareApplication","name":"SurrealDB","url":"https://surrealdb.com/surrealdb"},{"@type":"SoftwareApplication","name":"Agent Memory","url":"https://surrealdb.com/agent-memory"}],"knowsAbout":["multi-model databases","document databases","graph databases","vector search","time-series databases","SurrealQL","Agent Memory","real-time databases","embedded databases","context layer","graph ontology","distributed database","knowledge graphs","distributed transaction protocols","highly-scalable databases"],"sameAs":["https://www.wikidata.org/wiki/Q124316308","https://github.com/surrealdb/surrealdb","https://twitter.com/surrealdb","https://www.youtube.com/@surrealdb","https://www.linkedin.com/company/surrealdb","https://discord.gg/surrealdb","https://www.reddit.com/r/surrealdb","https://www.instagram.com/surrealdb","https://medium.com/surrealdb","https://dev.to/surrealdb"]}
```
