#3027·typesense

Failed async-reference alias swap rollback is not persisted across restart

Author: cyppeCreated Aug 19, 2026Updated Sep 1, 2026
Labelsbug

Summary

When an alias used by an async_reference is swapped to an incompatible collection, Typesense returns an error and appears to roll the alias back in memory. However, the rejected alias target is persisted. After restarting Typesense, the alias points to the rejected target and JOINs using the reference break.

This was reproduced twice, including once in a fresh isolated container.

Environment

  • Typesense: 31.0.rc14
  • Docker image: typesense/typesense:31.0.rc14
  • Image digest: sha256:6f63c3de844ce3c399dee23e04530f38e80a7462de621f1c8444245325320baa
  • Single-node Docker deployment

Reproduction

  1. Create parent_v1 with a code string field.
  2. Create alias parent -> parent_v1.
  3. Create children with:
json
{
  "name": "parent_code",
  "type": "string",
  "reference": "parent.code",
  "async_reference": true
}
  1. Index parent_v1 document {"id":"p1","code":"ok"} and child document {"id":"c1","parent_code":"ok"}.
  2. Create parent_bad with only an other string field, so it does not contain the referenced code field.
  3. Attempt to swap the alias:
http
PUT /aliases/parent

{"collection_name":"parent_bad"}

Typesense returns HTTP 500:

json
{"message":"Referenced field `code` not found in the collection `parent_bad`."}
  1. Before restart, verify that rollback appears successful:
  • GET /aliases/parent returns parent_v1.
  • The JOIN still returns the child and referenced parent.
  • The child helper field still resolves to sequence ID 0.
  1. Restart Typesense and check again.

Actual behavior after restart

  • GET /aliases/parent now returns parent_bad.
  • The child's parent_code_sequence_id becomes the unresolved sentinel 4294967295.
  • The JOIN fails with:
Referenced collection `parent` not found.

Relevant startup log:

Field `code` not found in the collection `parent_bad` which is referenced in `children.parent_code`.

Code pointer / possible fix direction

The risky ordering appears to be in CollectionManager::upsert_symlink():

A possible direction is to preflight the full target-rebind plan (including referenced-field validation) before persisting the candidate alias, or otherwise delay the durable alias write until rebind succeeds. A regression test should reload/restart from the same store after a failed swap, not only assert the in-memory alias immediately after the error.

Expected behavior

If the alias update returns an error, the rollback should be durable. Both the in-memory alias and persisted alias should continue pointing to parent_v1, including after restart or snapshot restoration.

Impact

A deployment can receive an error and verify that the old alias and JOINs still work, yet a later restart silently activates the rejected target and breaks reference-based searches. This makes the rollback response misleading and turns a rejected schema deployment into a delayed outage.

Related

  • #2827
  • PR #2963, which added alias-target rebind validation and rollback behavior

The original successful alias-swap scenarios from #2827 work correctly in 31.0.rc14; this report is specifically about persistence after a failed swap.