{"record":{"id":"abaef6dbb58b3438","repo":"siyuan-note/siyuan","slug":"relation-requires-a-relation-field","errorCode":null,"errorMessage":"relation requires a relation field","messagePattern":"relation requires a relation field","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_key_config.go","lineNumber":268,"sourceCode":"\t\tcolor = *config.Color\n\t\tif attrView.FilterColorValue(color) != color {\n\t\t\treturn errors.New(\"invalid option color\")\n\t\t}\n\t}\n\tif nil != config.Desc {\n\t\tdesc = *config.Desc\n\t}\n\tif name == option.Name {\n\t\top.Data = []*av.SelectOption{{Name: name, Color: color, Desc: desc}}\n\t\treturn updateAttributeViewColumnOptions(op)\n\t}\n\top.Data = map[string]any{\"oldName\": option.Name, \"newName\": name, \"newColor\": color, \"newDesc\": desc}\n\treturn updateAttributeViewColumnOption(op)\n}\n\nfunc updateAttributeViewKeyRelation(key *av.Key, op *Operation, value any) error {\n\tif av.KeyTypeRelation != key.Type {\n\t\treturn errors.New(\"relation requires a relation field\")\n\t}\n\tvar config struct {\n\t\tAvID        string `json:\"avID\"`\n\t\tIsTwoWay    *bool  `json:\"isTwoWay\"`\n\t\tBackKeyName string `json:\"backKeyName\"`\n\t}\n\tif err := decodeAttributeViewKeyConfig(value, &config); nil != err {\n\t\treturn err\n\t}\n\tif nil == config.IsTwoWay || \"\" == config.AvID {\n\t\treturn errors.New(\"relation requires avID and isTwoWay\")\n\t}\n\tdest, err := av.ParseAttributeView(config.AvID)\n\tif nil != err {\n\t\treturn err\n\t}\n\t// 先认证已有目标，避免无法读取旧关联时静默跳过回链清理。\n\tif nil != key.Relation && \"\" != key.Relation.AvID {","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_key_config.go#L250-L286","documentation":"updateAttributeViewKeyRelation handles updates to relation-type keys. This error is thrown when the target key's type is not av.KeyTypeRelation, meaning the caller attempted to apply relation configuration (avID, isTwoWay, backKeyName) to a non-relation field such as text, number, or select.","triggerScenarios":"Calling UpdateAttributeViewKeyConfig with operation targeting relation config against a key whose Type != KeyTypeRelation.","commonSituations":"Using a stale key ID after the field type was changed; confusing a rollup key with a relation key; scripted config that assumes all keys support relation settings.","solutions":["Verify the key's type is av.KeyTypeRelation before sending relation configuration","Create a new relation field if a relation is needed, instead of reconfiguring an existing non-relation key","Re-fetch the key by ID to confirm its current type (it may have changed)"],"exampleFix":"// before\nUpdateAttributeViewKeyConfig(avID, viewID, &conf.AttributeViewKeyConfig{KeyID: textKeyID, Relation: relCfg})\n// after\nif key, err := attrView.GetKey(textKeyID); err == nil && key.Type == av.KeyTypeRelation {\n    UpdateAttributeViewKeyConfig(avID, viewID, &conf.AttributeViewKeyConfig{KeyID: textKeyID, Relation: relCfg})\n}","handlingStrategy":"type-guard","validationCode":"// Verify key type via the API before sending relation config\nconst key = av.keys.find(k => k.id === keyID);\nconst isRelation = key && key.type === 'relation';","typeGuard":"function isRelationKey(key) {\n  return !!key && key.type === 'relation';\n}","tryCatchPattern":"try {\n  await api.updateAttributeViewKeyConfig({avID, keyID, relation: cfg});\n} catch (e) {\n  if (e.message.includes('relation requires a relation field')) {\n    // key is not a relation; create a relation key instead\n  }\n}","preventionTips":["Re-read the key's type immediately before config updates","Never reuse cached key IDs after type changes","Keep relation config in a code path only reachable for relation keys"],"tags":["attribute-view","relation","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"}