{"record":{"id":"a83bcc589adff374","repo":"toeverything/AFFiNE","slug":"invalid-key-for-key","errorCode":null,"errorMessage":"Invalid key for: ${key}","messagePattern":"Invalid key for: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/reactive/flat-native-y/utils.ts","lineNumber":41,"sourceCode":"\nexport function isEmptyObject(obj: UnRecord): boolean {\n  return Object.keys(obj).length === 0;\n}\n\nexport function deleteEmptyObject(\n  obj: UnRecord,\n  key: string,\n  parent: UnRecord\n): void {\n  if (isEmptyObject(obj)) {\n    delete parent[key];\n  }\n}\n\nexport function getFirstKey(key: string): string {\n  const result = key.split('.').at(0);\n  if (!result) {\n    throw new Error(`Invalid key for: ${key}`);\n  }\n  return result;\n}\n\nexport function bindOnChangeIfNeed(value: unknown, onChange: () => void): void {\n  if (value instanceof Text || Boxed.is(value)) {\n    value.bind(onChange);\n  }\n}\n","sourceCodeStart":23,"sourceCodeEnd":51,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/blocksuite/framework/store/src/reactive/flat-native-y/utils.ts#L23-L51","documentation":"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.","triggerScenarios":"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:.'.","commonSituations":"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.","solutions":["Sanitize prop keys before writing them to the Yjs map: drop or rename keys that are empty or start with '.'.","Fix the producer/transformer to never emit empty path segments when building dotted flat keys.","Add a guard at the loader boundary that filters out yMap entries whose keyWithoutPrefix is empty.","When constructing dotted keys programmatically, filter out empty segments before joining."],"exampleFix":"// before: building a flat key from possibly-empty segments\nyBlock.set(`prop:${segment}`, value); // segment can be ''\n\n// after: validate the segment\nif (!segment) return;\nyBlock.set(`prop:${segment}`, value);","handlingStrategy":"validation","validationCode":"export function assertValidDottedKey(key: string): void {\n  if (!key || !key.split('.').at(0)) {\n    throw new Error(`Invalid flat key: '${key}'`);\n  }\n}\n\n// before writing into a flat-data yBlock\nconst path = keyWithoutPrefix(rawKey);\nassertValidDottedKey(path);\nyBlock.set(`prop:${path}`, value);","typeGuard":"export const isValidDottedKey = (key: string): boolean =>\n  key.length > 0 && Boolean(key.split('.').at(0));","tryCatchPattern":"// getFirstKey throws a plain Error (no ErrorCode)\ntry {\n  const first = getFirstKey(key);\n} catch (e) {\n  if (e instanceof Error && /Invalid key for/.test(e.message)) {\n    // skip this malformed entry rather than abort initialization\n    return;\n  } else throw e;\n}","preventionTips":["Filter empty segments when building dotted flat keys programmatically.","Validate prop keys at the loader boundary.","Never write 'prop:' (prefix-only) or 'prop:.' into flat-data yBlocks.","Sanitize external data before importing into flat-data blocks."],"tags":["reactive-proxy","flat-data","validation","invariant"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}