{"record":{"id":"509c847280bd7625","repo":"facebook/lexical","slug":"invalid-state-key-key","errorCode":null,"errorMessage":"Invalid state key: ${key}","messagePattern":"Invalid state key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/lexical-yjs/src/SyncV2.ts","lineNumber":970,"sourceCode":"\nconst propertiesToAttributes = (node: LexicalNode, meta: BindingV2) => {\n  const defaultProperties = getDefaultNodeProperties(node, meta);\n  const attrs: Record<string, unknown> = {};\n  Object.entries(defaultProperties).forEach(([property, defaultValue]) => {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    const value = (node as any)[property];\n    if (value !== defaultValue) {\n      attrs[property] = value;\n    }\n  });\n  return attrs;\n};\n\nconst STATE_KEY_PREFIX = 's_';\nconst stateKeyToAttrKey = (key: string): `s_${string}` => `s_${key}`;\nconst attrKeyToStateKey = (key: string) => {\n  if (!key.startsWith(STATE_KEY_PREFIX)) {\n    throw new Error(`Invalid state key: ${key}`);\n  }\n  return key.slice(STATE_KEY_PREFIX.length);\n};\n\nconst stateToAttributes = (node: LexicalNode) => {\n  const state = node.__state;\n  if (!state) {\n    return {};\n  }\n  const [unknown = {}, known] = state.getInternalState();\n  const attrs: Record<string, unknown> = {};\n  for (const [k, v] of Object.entries(unknown)) {\n    attrs[stateKeyToAttrKey(k)] = v;\n  }\n  for (const [stateConfig, v] of known) {\n    attrs[stateKeyToAttrKey(stateConfig.key)] = stateConfig.unparse(v);\n  }\n  return attrs;","sourceCodeStart":952,"sourceCodeEnd":988,"githubUrl":"https://github.com/facebook/lexical/blob/76a22dcba981b46119fcec45f935270707e26a57/packages/lexical-yjs/src/SyncV2.ts#L952-L988","documentation":"SyncV2 stores Lexical node state (node.__state) in Yjs element attributes under keys prefixed with 's_'. attrKeyToStateKey converts an attribute key back to a state key and throws if the attribute does not carry the required prefix. This guards against reading foreign attributes (user data, other libs) as editor state.","triggerScenarios":"attrKeyToStateKey is called on an attribute key from a Yjs XmlElement that does not start with 's_' — i.e. stateToAttributes/attribute iteration encounters an attribute written outside the state-key convention.","commonSituations":"Hand-edited or migrated Yjs documents with attributes lacking the 's_' prefix; a custom sync integration writing raw attribute names; version skew where the prefix scheme changed between library versions.","solutions":["Ensure all attributes written to shared Yjs elements that represent node state use the stateKeyToAttrKey helper (adds the 's_' prefix).","Filter the attribute map before conversion: only pass keys matching /^s_/ to attrKeyToStateKey.","Check for version mismatch between the code writing attributes and the code reading them; upgrade both clients.","If migrating old documents, run a one-time migration to rename attributes to the prefixed form."],"exampleFix":"// before\nconst stateKey = attrKeyToStateKey(attrName);\n// after\nif (attrName.startsWith('s_')) {\n  const stateKey = attrKeyToStateKey(attrName);\n}","handlingStrategy":"validation","validationCode":"function isStateAttr(key: string): boolean {\n  return typeof key === 'string' && key.startsWith('s_');\n}\nObject.keys(attrs).filter(isStateAttr).map(attrKeyToStateKey);","typeGuard":"function isStateAttrKey(key: string): key is `s_${string}` {\n  return key.startsWith('s_');\n}","tryCatchPattern":"try {\n  stateKey = attrKeyToStateKey(key);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid state key')) {\n    console.warn('Skipping non-state attribute', key);\n    return;\n  }\n  throw e;\n}","preventionTips":["Always write node state attributes through stateKeyToAttrKey.","Filter attribute maps to the s_ prefix before parsing.","Version-check documents written by older library versions."],"tags":["yjs","collaboration","serialization","state-management"],"backgroundTag":"invalid-state-key-prefix","analyzedSha":"76a22dcba981b46119fcec45f935270707e26a57","analyzedAt":"2026-08-31T21:45:54.792Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}