{"record":{"id":"3d5df2d18a87970c","repo":"siyuan-note/siyuan","slug":"filter-group-requires-and-or-combination","errorCode":null,"errorMessage":"filter group requires and/or combination","messagePattern":"filter group requires and/or combination","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_key_config.go","lineNumber":165,"sourceCode":"\tif nil != err {\n\t\treturn err\n\t}\n\tvar filters []*av.ViewFilter\n\tif err = decodeAttributeViewKeyConfig(value, &filters); nil != err {\n\t\treturn err\n\t}\n\tif err = av.ValidateFilterDepth(filters); nil != err {\n\t\treturn err\n\t}\n\tvar validate func([]*av.ViewFilter) error\n\tvalidate = func(items []*av.ViewFilter) error {\n\t\tfor _, filter := range items {\n\t\t\tif nil == filter {\n\t\t\t\treturn errors.New(\"filter must not be null\")\n\t\t\t}\n\t\t\tif filter.IsGroup() {\n\t\t\t\tif av.FilterCombinationAnd != filter.Combination && av.FilterCombinationOr != filter.Combination {\n\t\t\t\t\treturn errors.New(\"filter group requires and/or combination\")\n\t\t\t\t}\n\t\t\t\tif err := validate(filter.Filters); nil != err {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else if _, err := dest.GetKey(filter.Column); nil != err {\n\t\t\t\treturn fmt.Errorf(\"invalid filter target field: %s\", filter.Column)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn validate(filters)\n}\n\n// decodeAttributeViewKeyConfig 拒绝未知字段及空值，防止拼写错误被当作成功更新。\nfunc decodeAttributeViewKeyConfig(value, dest any) error {\n\tif nil == value {\n\t\treturn errors.New(\"configuration must not be null\")\n\t}","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_key_config.go#L147-L183","documentation":"This error is thrown when a filter group (a ViewFilter with nested Filters) is submitted with a Combination value that is neither FilterCombinationAnd nor FilterCombinationOr. SiYuan validates relation-filter groups before saving so that stored filter trees always have a well-defined boolean combination. It guards the API setAttributeViewKeyConfig for the 'filters'/'rollupFilters' settings.","triggerScenarios":"Calling UpdateAttributeViewKeyConfig (via the kernel API) with setting 'filters' or 'rollupFilters' where an element of the filter array has non-empty Filters (IsGroup() true) but Combination is empty, an unknown string, or any value other than 'And'/'Or'.","commonSituations":"Building the filter JSON payload by hand and omitting Combination on a nested group; serializing a group from plugin code with a misspelled combination value; API clients that copy a leaf filter shape (no combination) for groups.","solutions":["Set Combination to av.FilterCombinationAnd (\"And\") or av.FilterCombinationOr (\"Or\") on every group filter","Remove the nested Filters array if the entry was meant to be a leaf filter","Check the combination value for typos/case before sending"],"exampleFix":"// before\n{\"column\":\"\",\"combination\":\"and_all\",\"filters\":[{\"column\":\"20240101120000-abc\",\"value\":{\"content\":\"x\"}}]}\n// after\n{\"column\":\"\",\"combination\":\"And\",\"filters\":[{\"column\":\"20240101120000-abc\",\"value\":{\"content\":\"x\"}}]}","handlingStrategy":"validation","validationCode":"const isGroup = f => Array.isArray(f.filters) && f.filters.length > 0;\nfor (const f of filters) {\n  if (isGroup(f) && !(f.combination === \"And\" || f.combination === \"Or\")) {\n    throw new Error(`group filter needs combination And/Or, got ${f.combination}`);\n  }\n}","typeGuard":"const hasValidCombination = (f) => !isFilterGroup(f) || f.combination === \"And\" || f.combination === \"Or\";","tryCatchPattern":null,"preventionTips":["Always set combination on filters that carry a nested filters array","Reuse a shared filter-builder helper so groups are constructed with a combination by default"],"tags":["attribute-view","filter","validation"],"backgroundTag":"invalid-enum-value","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"}