All rules Rule SR001 · CN
SurrealDB · beta
critical
CN · Constraints & keys
free in the CLI, --engine=surrealdb

Why do SurrealDB updates fail after I add a field with a TYPE?

DEFINE FIELD with a required type on an existing table

Critical: SurrealDB refuses the migration, loses data, or leaks a credential.

What happens

Observed on SurrealDB 3.0.2: CREATE customer:1, then DEFINE FIELD region ON customer TYPE string succeeds, and the next UPDATE customer:1 SET name = 'b' fails with "Couldn't coerce value for field `region` of `customer:1`: Expected `string` but found `NONE`". The same happens with DEFAULT 'eu'; DEFAULT ALWAYS, VALUE, option<string> and string | none all let the update through.

Why it is dangerous on a populated table

SurrealDB checks a schema change against new writes, not the records already stored, so an empty table hides it: on a populated one every old record is a write waiting to fail, and whole-table work runs for the length of the table in one transaction.

Fires on

DEFINE FIELD region ON TABLE customer TYPE string;

The safe pattern

Make the field optional (option<T>) until every record carries it, give it DEFAULT ALWAYS so SurrealDB fills it on every write, or backfill it in the same migration.

DEFINE FIELD region ON TABLE customer TYPE string DEFAULT ALWAYS 'eu';

Fixtures

The rule ships with these files and the test suite runs them on every change: the first set must fire, the second must stay silent.

Fires (3)

if not exists
DEFINE FIELD IF NOT EXISTS loyalty_points ON customer TYPE int;
plain default
-- DEFAULT only fills the field on CREATE; existing records still lack it.
DEFINE FIELD region ON TABLE customer TYPE string DEFAULT 'eu';
required type
DEFINE FIELD region ON TABLE customer TYPE string;

Stays silent (7)

backfilled
DEFINE FIELD region ON TABLE customer TYPE string;
UPDATE customer SET region = 'eu' WHERE region IS NONE;
computed value
DEFINE FIELD updated_at ON TABLE customer TYPE datetime VALUE time::now();
default always
DEFINE FIELD region ON TABLE customer TYPE string DEFAULT ALWAYS 'eu';
nested path
DEFINE FIELD address.city ON TABLE customer TYPE string;
new table
DEFINE TABLE customer SCHEMAFULL;
DEFINE FIELD region ON TABLE customer TYPE string;
optional
DEFINE FIELD region ON TABLE customer TYPE option<string>;
union with none
DEFINE FIELD region ON TABLE customer TYPE string | none;

How to check locally

Catch this before it ships

SurrealDB support is in beta: this rule runs locally in the free CLI over .surql migrations, static only, and not in the hosted service yet. No install, nothing leaves your machine:

npx bolvrk check migration.surql --engine=surrealdb