{"record":{"id":"f2c729ff7f74a0d8","repo":"siyuan-note/siyuan","slug":"previous-id-and-next-id-cannot-both-be-specified","errorCode":null,"errorMessage":"previous ID and next ID cannot both be specified","messagePattern":"previous ID and next ID cannot both be specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_create.go","lineNumber":55,"sourceCode":"\tIcon string `json:\"icon,omitempty\"`\n}\n\n// AttributeViewCreateResult 描述创建数据库后可供调用方继续操作的标识和数据。\ntype AttributeViewCreateResult struct {\n\tBlockID       string\n\tAvID          string\n\tViewID        string\n\tAttributeView *av.AttributeView\n}\n\n// CreateAttributeViewDatabase 创建数据库块和对应属性视图，并按照 keySpecs 的顺序初始化字段。\nfunc CreateAttributeViewDatabase(parentID, previousID, nextID, name, primaryKeyName string, layout av.LayoutType,\n\tkeySpecs []*AttributeViewCreateKey) (ret *AttributeViewCreateResult, err error) {\n\tif \"\" == parentID {\n\t\treturn nil, errors.New(\"parent ID is required\")\n\t}\n\tif \"\" != previousID && \"\" != nextID {\n\t\treturn nil, errors.New(\"previous ID and next ID cannot both be specified\")\n\t}\n\tif \"\" == previousID && \"\" == nextID {\n\t\tif err = treenode.CheckContainerParent(parentID); nil != err {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif \"\" == layout {\n\t\tlayout = av.LayoutTypeTable\n\t}\n\tswitch layout {\n\tcase av.LayoutTypeTable, av.LayoutTypeGallery, av.LayoutTypeKanban:\n\tdefault:\n\t\treturn nil, av.ErrWrongLayoutType\n\t}\n\n\tpreparedKeys := make([]*av.Key, 0, len(keySpecs))\n\tfor _, spec := range keySpecs {\n\t\tif nil == spec || \"\" == strings.TrimSpace(spec.Name) || \"\" == strings.TrimSpace(spec.Type) {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/attribute_view_create.go#L37-L73","documentation":"A new database block can be appended to a parent, or inserted relative to one sibling (before previousID or after nextID), but not positioned between two siblings simultaneously. Supplying both previousID and nextID is ambiguous, so the function rejects the call.","triggerScenarios":"Calling CreateAttributeViewDatabase with both previousID and nextID set to non-empty values.","commonSituations":"A caller that forwards UI selection state (which may contain both anchor directions) directly into the API; code that builds insertion options from a struct with optional fields but forgets to clear the unused sibling.","solutions":["Pass only one of previousID or nextID, leaving the other as \"\"","If you truly need to insert between two blocks, pass the left neighbor as previousID and empty nextID (or the right neighbor as nextID)","Add caller-side logic that clears the opposite field when one anchor is chosen"],"exampleFix":"// before\nret, err := model.CreateAttributeViewDatabase(parentID, prevID, nextID, name, pk, layout, keys)\n// after\nif prevID != \"\" {\n    nextID = \"\" // only one sibling anchor is allowed\n}\nret, err := model.CreateAttributeViewDatabase(parentID, prevID, nextID, name, pk, layout, keys)","handlingStrategy":"validation","validationCode":"if previousID != \"\" && nextID != \"\" {\n    return errors.New(\"specify only one of previousID or nextID\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model insertion as {append | after prev | before next} and clear the unused anchor field","Never forward raw multi-field UI selection state into the API without narrowing it","Write a small normalizeInsertion helper used by all call sites"],"tags":["api","validation","attribute-view","go"],"backgroundTag":"mutually-exclusive-options","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"}