toeverything/AFFiNE · error · BlockSuiteError
DatabaseBlockError
DatabaseBlockError
Error message
unexpected type unify, type var reference
What it means
Internal invariant of the data-view logical type engine. TypeVarReferenceInstance (type-variable.ts:20-45) is a placeholder produced by tRef(name); it must be substituted with a concrete TypeInstance via subst(TypeVarContext) before type unification. Its unify() always throws 'unexpected type unify, type var reference', meaning a type variable was never bound (its name missing from the context) when unification ran.
Solutions
- Ensure every tVarDefine name referenced by tRef has an entry in the TypeVarContext before unifying
- Call subst first, verify it returned a concrete type, and only then unify
- Use the built-in guard tv.typeVarReference.is(type) to reject placeholders before calling unify
Example fix
// before
const ok = unify(ctx, tRef('T'), concreteType); // throws if 'T' unbound
// after
const resolved = substitute(tRef('T'), ctx);
if (!resolved) throw new Error(`unbound type variable T`); Defensive patterns
Strategy: type-guard
Validate before calling
import { tv } from './type-variable.js';
// substitute placeholders and verify binding before unify
const substResult = typeInstance.subst(typeVarContext);
if (tv.typeVarReference.is(typeInstance) && !substResult) {
throw new Error(`unbound type variable: ${typeInstance.varName}`);
} Type guard
tv.typeVarReference.is = (type: TypeInstance): type is TypeVarReferenceInstance => type.name === '__TypeVarReference'; // usage: never call unify on a value passing this guard
Try / catch
try {
unify(ctx, left, right);
} catch (e) {
if (e instanceof BlockSuiteError && e.code === BlockSuiteError.ErrorCode.DatabaseBlockError) {
// binding bug: report which type var was left unsubstituted
}
throw e;
} Prevention
- Always bind a tVarDefine entry for every tRef name before unification
- Run subst() to a fixpoint and check for undefined before unify
- Treat this throw as an internal assertion failure — fix the caller, don't catch it in production paths
When it happens
Trigger: Unifying a TypeInstance that is still a __TypeVarReference because ctx[varName] was never defined (subst returned undefined); building logical expressions where tRef('x') has no matching tVar define entry in the TypeVarContext; calling unify directly on a freshly created tRef without substitution.
Common situations: Writing custom logical expressions / property type constraints against the data-view type engine; refactoring type-variable plumbing so define and reference names diverge; serialize/deserialize of expressions losing the define side of the context.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/93d228ef60f15f92.
Report an issue: GitHub.
Appendix: source
Thrown at blocksuite/affine/data-view/src/core/logical/type-variable.ts:36
}
export class TypeVarReferenceInstance<
Name extends string = string,
> implements TypeInstance {
readonly _validate = unknownSchema;
readonly _valueType = undefined as unknown;
readonly name = '__TypeVarReference';
constructor(readonly varName: Name) {}
subst(ctx: TypeVarContext): void | TypeInstance {
return ctx[this.varName]?.type;
}
unify(_ctx: TypeVarContext, _type: TypeInstance, _unify: Unify): boolean {
throw new BlockSuiteError(
ErrorCode.DatabaseBlockError,
'unexpected type unify, type var reference'
);
}
valueValidate(_value: unknown): _value is unknown {
return true;
}
}
export const tv = {
typeVarDefine: {
create: <
Name extends string = string,
Type extends TypeInstance = TypeInstance,
>(
name: Name,
typeConstraint?: TypeView on GitHub (pinned to b4c8548c09)