siyuan-note/siyuan · error
view state patch contains too many entries
Error message
view state patch contains too many entries
What it means
PatchViewState limits the total number of entries in a single patch (values plus removeKeys) to maxViewStatePatchCount. Patches exceeding that cap are rejected before validation and merge, keeping view-state storage bounded and patches small.
Source
Thrown at kernel/model/view_state.go:87
defer viewStateStorageLock.Unlock()
storage, err := getViewStateStorage()
if err != nil {
return nil, err
}
state := storage.Views[key]
if nil == state || nil == state.Data {
return map[string]any{}, nil
}
return cloneViewStateData(state.Data)
}
func PatchViewState(key string, values map[string]any, removeKeys []string) (ret map[string]any, err error) {
if err = validateViewStateKey(key); err != nil {
return
}
if maxViewStatePatchCount < len(values)+len(removeKeys) {
return nil, errors.New("view state patch contains too many entries")
}
for valueKey := range values {
if err = validateViewStateDataKey(valueKey); err != nil {
return nil, err
}
}
for _, removeKey := range removeKeys {
if err = validateViewStateDataKey(removeKey); err != nil {
return nil, err
}
}
patch := &viewStatePatch{Values: values, RemoveKeys: removeKeys}
patchData, marshalErr := gulu.JSON.MarshalJSON(patch)
if nil != marshalErr {
return nil, marshalErr
}
if maxViewStatePatchBytes < len(patchData) {
return nil, errors.New("view state patch is too large")View on GitHub (pinned to 8641553a1f)
Solutions
- Split the patch into multiple sequential PatchViewState calls, each under the entry-count cap
- Aggregate many flags into a single JSON value under one data key instead of one key per flag
- Trim the patch: remove only stale keys and set only changed values
Example fix
// before
ret, err := model.PatchViewState(key, allValues, allRemoveKeys) // hundreds of entries
// after
const batchSize = 50
for i := 0; i < len(allValues); i += batchSize {
if _, err = model.PatchViewState(key, subMap(allValues, i, batchSize), nil); err != nil {
break
}
} Defensive patterns
Strategy: try-catch
Validate before calling
func isPatchSizeOK(values map[string]any, removeKeys []string) bool {
return len(values)+len(removeKeys) <= model.MaxViewStatePatchCount
} Try / catch
_, err := model.PatchViewState(key, values, removeKeys)
if err != nil && strings.Contains(err.Error(), "too many entries") {
// split into smaller batches and retry
for batch := range chunkMap(values, 50) {
if _, err = model.PatchViewState(key, batch, nil); err != nil {
return err
}
}
return model.PatchViewState(key, nil, removeKeys)
}
return err Prevention
- Keep the number of view-state keys per view small; aggregate related flags into one object value
- Batch large patches into chunks safely below the entry cap
- Only include keys that actually changed instead of re-sending full state each time
When it happens
Trigger: Calling PatchViewState with len(values)+len(removeKeys) greater than maxViewStatePatchCount, e.g. one call that sets or removes hundreds of data keys at once for the same view-state key.
Common situations: Persisting a large bulk state blob (many keys) in one patch instead of a few aggregated values; a plugin syncing many UI flags in one call; batch cleanup that removes a long list of keys in a single removeKeys slice.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- view state patch is too large
- Maximum length is limited to 512 characters
- invalid view state key
- wrong layout type
- filter nesting depth exceeds the maximum allowed
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/58df254ec0ed303d.
Report an issue: GitHub.