{"record":{"id":"a2613a1c1cfab490","repo":"siyuan-note/siyuan","slug":"rollup-requires-a-rollup-field","errorCode":null,"errorMessage":"rollup requires a rollup field","messagePattern":"rollup requires a rollup field","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_key_config.go","lineNumber":319,"sourceCode":"\t\t\t}\n\t\t\tif \"\" == config.BackKeyName {\n\t\t\t\top.Name = backKey.Name\n\t\t\t}\n\t\t}\n\t}\n\terr = updateAttributeViewColRelation(op)\n\tif nil == err {\n\t\tReloadAttrView(config.AvID)\n\t\tif nil != key.Relation && key.Relation.AvID != config.AvID {\n\t\t\tReloadAttrView(key.Relation.AvID)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc updateAttributeViewKeyRollup(attrView *av.AttributeView, key *av.Key, op *Operation, value any) error {\n\tif av.KeyTypeRollup != key.Type {\n\t\treturn errors.New(\"rollup requires a rollup field\")\n\t}\n\tvar config struct {\n\t\tRelationKeyID string  `json:\"relationKeyID\"`\n\t\tKeyID         string  `json:\"keyID\"`\n\t\tOperator      *string `json:\"operator\"`\n\t}\n\tif err := decodeAttributeViewKeyConfig(value, &config); nil != err {\n\t\treturn err\n\t}\n\tif nil == config.Operator || !slices.Contains(AttributeViewKeyRollupOperators, *config.Operator) {\n\t\treturn errors.New(\"rollup requires a supported operator\")\n\t}\n\trelationKey, err := attrView.GetKey(config.RelationKeyID)\n\tif nil != err || av.KeyTypeRelation != relationKey.Type || nil == relationKey.Relation {\n\t\treturn errors.New(\"rollup requires a configured relation field\")\n\t}\n\tdest, err := av.ParseAttributeView(relationKey.Relation.AvID)\n\tif nil != err {","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_key_config.go#L301-L337","documentation":"updateAttributeViewKeyRollup applies rollup (aggregate) configuration to a key. This error is thrown when the target key's type is not av.KeyTypeRollup, i.e. the caller attempted to set rollup configuration on a field that is not a rollup field.","triggerScenarios":"Calling UpdateAttributeViewKeyConfig with rollup config (relationKeyID, keyID, operator) against a key whose Type != KeyTypeRollup.","commonSituations":"Reusing a key ID from a changed/deleted field; confusing relation and rollup keys (a rollup references a relation key but is itself a distinct type); copy-pasted config code applying rollup settings to relation keys.","solutions":["Confirm the key type is av.KeyTypeRollup before sending rollup configuration","Create a new rollup field if aggregation is needed on a non-rollup key","Re-fetch the key to verify its type — it may have been modified since you cached it"],"exampleFix":"// before\nUpdateAttributeViewKeyConfig(avID, viewID, &conf.AttributeViewKeyConfig{KeyID: relKeyID, Rollup: rollupCfg})\n// after\nif key, _ := attrView.GetKey(relKeyID); key.Type == av.KeyTypeRollup {\n    UpdateAttributeViewKeyConfig(avID, viewID, &conf.AttributeViewKeyConfig{KeyID: relKeyID, Rollup: rollupCfg})\n}","handlingStrategy":"type-guard","validationCode":"const key = av.keys.find(k => k.id === keyID);\nconst isRollup = key && key.type === 'rollup';","typeGuard":"function isRollupKey(key) {\n  return !!key && key.type === 'rollup';\n}","tryCatchPattern":"try {\n  await api.updateAttributeViewKeyConfig({avID, keyID, rollup: cfg});\n} catch (e) {\n  if (e.message.includes('rollup requires a rollup field')) {\n    // target key is not a rollup; create one instead\n  }\n}","preventionTips":["Only send rollup config for keys of type rollup","Verify key type right before the update call","Distinguish relation keys from rollup keys in your data model"],"tags":["attribute-view","rollup","type-mismatch"],"backgroundTag":"incompatible-source-type","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}