toeverything/AFFiNE · error · Error

Invalid key for: ${key}

Error message

Invalid key for: ${key}

What it means

Thrown by getFirstKey in flat-native-y/utils when key.split('.').at(0) returns undefined, i.e. the key is empty or contains no usable first segment. getFirstKey is used to derive the top-level prop name from a dotted flat key; an empty/malformed key has no valid first segment so the function refuses rather than returning undefined and silently corrupting the proxy shape. Note this is a plain Error (no ErrorCode), so it is a lower-level invariant violation.

Source

Thrown at blocksuite/framework/store/src/reactive/flat-native-y/utils.ts:41

export function isEmptyObject(obj: UnRecord): boolean {
  return Object.keys(obj).length === 0;
}

export function deleteEmptyObject(
  obj: UnRecord,
  key: string,
  parent: UnRecord
): void {
  if (isEmptyObject(obj)) {
    delete parent[key];
  }
}

export function getFirstKey(key: string): string {
  const result = key.split('.').at(0);
  if (!result) {
    throw new Error(`Invalid key for: ${key}`);
  }
  return result;
}

export function bindOnChangeIfNeed(value: unknown, onChange: () => void): void {
  if (value instanceof Text || Boxed.is(value)) {
    value.bind(onChange);
  }
}

View on GitHub (pinned to 26c515e050)

Solutions

  1. Sanitize prop keys before writing them to the Yjs map: drop or rename keys that are empty or start with '.'.
  2. Fix the producer/transformer to never emit empty path segments when building dotted flat keys.
  3. Add a guard at the loader boundary that filters out yMap entries whose keyWithoutPrefix is empty.
  4. When constructing dotted keys programmatically, filter out empty segments before joining.

Example fix

// before: building a flat key from possibly-empty segments
yBlock.set(`prop:${segment}`, value); // segment can be ''

// after: validate the segment
if (!segment) return;
yBlock.set(`prop:${segment}`, value);
Defensive patterns

Strategy: validation

Validate before calling

export function assertValidDottedKey(key: string): void {
  if (!key || !key.split('.').at(0)) {
    throw new Error(`Invalid flat key: '${key}'`);
  }
}

// before writing into a flat-data yBlock
const path = keyWithoutPrefix(rawKey);
assertValidDottedKey(path);
yBlock.set(`prop:${path}`, value);

Type guard

export const isValidDottedKey = (key: string): boolean =>
  key.length > 0 && Boolean(key.split('.').at(0));

Try / catch

// getFirstKey throws a plain Error (no ErrorCode)
try {
  const first = getFirstKey(key);
} catch (e) {
  if (e instanceof Error && /Invalid key for/.test(e.message)) {
    // skip this malformed entry rather than abort initialization
    return;
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getFirstKey(''), getFirstKey('.'), or any key whose split on '.' yields only empty strings; produced by a flat-data transformer or external Yjs producer that emits prop keys like 'prop:' (prefix only) or 'prop:.'.

Common situations: Hand-built Yjs maps with malformed prop keys; a migration that strips a key down to its prefix; an encoder bug that joins an empty path; external data import with empty property names.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/a83bcc589adff374. Report an issue: GitHub.