toeverything/AFFiNE · error · Error
Invalid key for
Error message
Invalid key for: ${key} What it means
getFirstKey is used throughout the flat reactive layer (proxy set/delete paths and Y event handling) to reduce a dotted prop path like 'a.b.c' to its first segment. An empty path ('') or one with a leading dot ('.b') splits to an empty first segment, which is falsy, so the util throws a plain Error — the key was built wrong before it reached the flat model.
Solutions
- Validate generated keys before prop access: non-empty, no leading/trailing dots, no '..' sequences
- Audit template-literal key construction (e.g. `${prefix}.${name}`) for empty prefix/name and use a join helper that filters empties
- Repair malformed 'prop:'-prefixed entries in the Y map by deleting or renaming them
Example fix
// before
const path = `${base}.${name}`; // base is '' -> path '.title' -> Invalid key for: .title
model._props[path] = value;
// after
const path = [base, name].filter(Boolean).join('.'); // 'title'
if (path) {
model._props[path] = value;
} Defensive patterns
Strategy: validation
Validate before calling
function isValidPropPath(key: string): boolean {
return key.length > 0 && !key.startsWith('.') && !key.endsWith('.') && !key.includes('..');
}
const path = [base, name].filter(Boolean).join('.');
if (isValidPropPath(path)) {
model._props[path] = value;
} Type guard
const isValidPropPath = (key: string): boolean =>
key.length > 0 && !key.startsWith('.') && !key.endsWith('.') && !key.includes('..'); Try / catch
try {
model._props[path] = value;
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid key for:')) {
// rebuild the path from non-empty segments and retry
} else {
throw e;
}
} Prevention
- Build dotted prop paths with a helper that filters out empty segments
- Validate computed keys (non-empty, no leading/trailing dot) before prop access
- Sanitize imported prop names: reject blank and dot-padded keys at the boundary
When it happens
Trigger: Setting, deleting, or remotely updating a flat-model prop whose computed key is '' or starts/ends with '.' — e.g. template strings concatenating an undefined segment (`prop:${base}.x` with base empty), or a Y event arriving for a key literally named 'prop:'.
Common situations: Programmatic prop-path builders that skip empty segments; importing data with blank prop names; edge-case Y updates from peers with malformed keys.
Related errors
- ReactiveProxyError
- ErrorCode.DefaultRuntimeError
- ErrorCode.ModelCRUDError
- props is only supported in flat data model
- 1
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/a83bcc589adff374.
Report an issue: GitHub.
Appendix: 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 b4c8548c09)