toeverything/AFFiNE · error · Error
a should be smaller than b
Error message
a should be smaller than b
What it means
generateKeyBetweenV2() in blocksuite/framework/std/src/utils/fractional-indexing.ts creates a sort key strictly between a and b (used for ordering edgeless/surface blocks). It validates that when both neighbors are given, a >= b must fail-fast — fractional indexing only defines a key for an open interval (a, b). A plain Error (not BlockSuiteError) is thrown because this is a caller contract violation.
Source
Thrown at blocksuite/framework/std/src/utils/fractional-indexing.ts:28
* make sure a and b are generated by this function.
*
* @param customPostfix custom postfix for the key, only letters and numbers are allowed
*/
export function generateKeyBetweenV2(a: string | null, b: string | null) {
const randomSize = 32;
function postfix(length: number = randomSize) {
const chars =
'123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const values = new Uint8Array(length);
crypto.getRandomValues(values);
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(values[i] % chars.length);
}
return result;
}
if (a !== null && b !== null && a >= b) {
throw new Error('a should be smaller than b');
}
// get the subkey in full key
// e.g.
// a0xxxx -> a
// a0x0xxxx -> a0x
function subkey(key: string | null) {
if (key === null) {
return null;
}
if (key.length <= randomSize + 1) {
// no subkey
return key;
}
const splitAt = key.substring(0, key.length - randomSize - 1);
return splitAt;
}
const aSubkey = subkey(a);
const bSubkey = subkey(b);View on GitHub (pinned to b4c8548c09)
Solutions
- Always call it as generateKeyBetweenV2(prevKey, nextKey) with prevKey strictly less than nextKey; use null for the open ends.
- Guard before calling: if (a !== null && b !== null && a >= b) throw/swap or fall back to inserting at an end.
- When keys come from stored data, re-validate ordering (sort neighbors before picking bounds).
- Never mix keys produced by different generators in the same index space.
Example fix
// before
const key = generateKeyBetweenV2(nextKey, prevKey);
// after
const key = generateKeyBetweenV2(prevKey, nextKey);
// or guard:
const key =
prevKey && nextKey && prevKey >= nextKey
? generateKeyBetweenV2(null, nextKey)
: generateKeyBetweenV2(prevKey, nextKey); Defensive patterns
Strategy: validation
Validate before calling
function safeGenerate(a: string | null, b: string | null): string {
if (a !== null && b !== null && a >= b) {
// caller mixed up order or keys are equal — insert at an end instead of crashing
return generateKeyBetweenV2(null, b);
}
return generateKeyBetweenV2(a, b);
} Prevention
- Always pass (prevKey, nextKey) in strictly increasing order; null means unbounded.
- Never reuse an existing key as both bounds (duplicate keys violate the invariant).
- Use only keys produced by generateKeyBetweenV2 in the same index space.
When it happens
Trigger: Calling generateKeyBetweenV2(a, b) with a >= b: passing (nextKey, prevKey) in the wrong order when inserting/moving an element, passing the same key for both arguments, or feeding legacy/hand-built keys that don't compare as expected lexicographically.
Common situations: Implementing drag-to-reorder in edgeless mode and swapping the prev/next key arguments; duplicating a block and reusing its key for both bounds; mixed key formats (some keys from the older generateNKeysBetween or hand-written) where lexicographic order surprises the caller.
Related errors
- ErrorCode.GfxBlockElementError
- ErrorCode.TransformerError
- a should be smaller than b
- ErrorCode.InlineEditorError
- ErrorCode.ValueNotExists
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/e87de899f58d7904.
Report an issue: GitHub.