{"record":{"id":"d6a34e9686f2f49f","repo":"siyuan-note/siyuan","slug":"invalid-view-state-data-key","errorCode":null,"errorMessage":"invalid view state data key","messagePattern":"invalid view state data key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/view_state.go","lineNumber":220,"sourceCode":"\t\t\treturn int64(len(keys) + 1)\n\t\t}\n\t\tif ret <= state.Updated {\n\t\t\tret = state.Updated + 1\n\t\t}\n\t}\n\treturn ret\n}\n\nfunc validateViewStateKey(key string) error {\n\tif \"\" == strings.TrimSpace(key) || 1024 < len(key) {\n\t\treturn errors.New(\"invalid view state key\")\n\t}\n\treturn nil\n}\n\nfunc validateViewStateDataKey(key string) error {\n\tif \"\" == strings.TrimSpace(key) || 2048 < len(key) {\n\t\treturn errors.New(\"invalid view state data key\")\n\t}\n\treturn nil\n}\n\nfunc pruneViewStates(views map[string]*ViewState) {\n\tif len(views) <= maxViewStateCount {\n\t\treturn\n\t}\n\n\tkeys := make([]string, 0, len(views))\n\tfor key := range views {\n\t\tkeys = append(keys, key)\n\t}\n\tsort.Slice(keys, func(i, j int) bool {\n\t\tleft, right := views[keys[i]], views[keys[j]]\n\t\tif left.Updated == right.Updated {\n\t\t\treturn keys[i] < keys[j]\n\t\t}","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/view_state.go#L202-L238","documentation":"validateViewStateDataKey rejects a view-state data key that is empty/whitespace-only or longer than 2048 bytes. The kernel stores per-view state payloads keyed by data keys, and keys beyond these limits would bloat or break the storage format, so PatchViewState refuses the write before persisting anything.","triggerScenarios":"Calling the view-state patch API (PatchViewState path) with a data key that is \"\" or all whitespace, or a string whose length exceeds 2048 characters.","commonSituations":"A plugin or frontend feature builds the data key by concatenating IDs/paths and accidentally produces an empty string when an ID is missing; or embeds a long document path/blob ID, URL, or serialized JSON that exceeds 2048 characters.","solutions":["Ensure the data key is non-empty and trimmed before calling the patch API.","Shorten the key: hash long identifiers (e.g. SHA-256 hex) instead of embedding full paths/URLs, keeping it under 2048 characters.","Log the failing key at the call site to identify which concatenation produced an empty or oversized value."],"exampleFix":"// before\nconst key = `${docPath}/${blockId}/${extra}`;\nawait patchViewState(viewId, key, data);\n\n// after\nconst key = [docPath, blockId, extra].filter(Boolean).join(\"/\").slice(0, 2048);\nif (!key.trim()) throw new Error(\"view state data key required\");\nawait patchViewState(viewId, key, data);","handlingStrategy":"validation","validationCode":"function isValidViewStateDataKey(key) {\n  return typeof key === \"string\" && key.trim().length > 0 && key.length <= 2048;\n}\nif (!isValidViewStateDataKey(key)) throw new Error(\"refusing to patch view state: bad data key\");","typeGuard":"const isViewStateDataKey = (v) => typeof v === \"string\" && v.trim() !== \"\" && v.length <= 2048;","tryCatchPattern":null,"preventionTips":["Hash long identifiers (paths, URLs) into short keys before use.","Filter out empty components when building composite keys.","Assert key validity in a small helper used by all view-state writes."],"tags":["validation","input-length","view-state"],"backgroundTag":"invalid-identifier-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}