{"record":{"id":"bc5152f873d6b9fa","repo":"siyuan-note/siyuan","slug":"unexpected-attribute-view-payload-in-block-operation-s","errorCode":null,"errorMessage":"unexpected attribute view payload in block operation [%s]","messagePattern":"unexpected attribute view payload in block operation \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/contract_block_transaction.go","lineNumber":16,"sourceCode":"package api\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/siyuan-note/siyuan/kernel/apicontract\"\n\t\"github.com/siyuan-note/siyuan/kernel/model\"\n)\n\nfunc blockOperationContract(operation *model.Operation) (*apicontract.BlockOperation, error) {\n\tif operation == nil {\n\t\treturn nil, nil\n\t}\n\tif operation.Srcs != nil || len(operation.CellUpdates) > 0 {\n\t\treturn nil, fmt.Errorf(\"unexpected attribute view payload in block operation [%s]\", operation.Action)\n\t}\n\tret := &apicontract.BlockOperation{\n\t\tAction:            operation.Action,\n\t\tID:                operation.ID,\n\t\tRootID:            operation.RootID,\n\t\tParentID:          operation.ParentID,\n\t\tPreviousID:        operation.PreviousID,\n\t\tNextID:            operation.NextID,\n\t\tBlockIDs:          operation.BlockIDs,\n\t\tBlockID:           operation.BlockID,\n\t\tDeckID:            operation.DeckID,\n\t\tAvID:              operation.AvID,\n\t\tSrcIDs:            operation.SrcIDs,\n\t\tIsDetached:        operation.IsDetached,\n\t\tName:              operation.Name,\n\t\tTyp:               operation.Typ,\n\t\tFormat:            operation.Format,\n\t\tKeyID:             operation.KeyID,","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/contract_block_transaction.go#L1-L34","documentation":"blockOperationContract converts kernel-side model.Operation values into the public apicontract.BlockOperation shape for block-only transactions. Block operations must carry DOM payload in Data and no attribute-view payload; if Srcs (attribute-view source value IDs) is non-nil or CellUpdates is non-empty, the operation is an av operation misclassified as a block operation, and conversion refuses with this error naming the offending Action.","triggerScenarios":"Building or replaying a transaction where an attribute-view operation (e.g. insertAttrViewBlock / setAttrViewCell with Srcs or CellUpdates) is placed in the DoOperations list that feeds blockOperationContract — e.g. a plugin mixing av operations into a block transaction, or a kernel bug mislabeling the operation kind.","commonSituations":"Plugin code pushing transactions with mixed operation types; contract conversion running over legacy transactions that combined block and av operations; a kernel upgrade changing operation payload shape so old stored transactions hit this path.","solutions":["Split the transaction: put attribute-view operations in a separate av transaction and block operations in a block-only transaction","Clear Srcs/CellUpdates for pure block operations — pass the DOM in Data and leave av fields nil","Check the operation's Action and route it through the matching contract converter (block vs av) instead of assuming block","If triggered by stored/replayed transactions, re-record or migrate them after upgrading the kernel"],"exampleFix":"// before\nops := []*model.Operation{\n  {Action: \"insert\", ID: id, Data: dom, Srcs: avSrcs},\n}\n// after\nblockOps := []*model.Operation{{Action: \"insert\", ID: id, Data: dom}}\navOps := []*model.Operation{{Action: \"insertAttrViewBlock\", Srcs: avSrcs}}\n// issue blockOps and avOps as separate transactions","handlingStrategy":"type-guard","validationCode":"const hasAvPayload = (op) => op && (Array.isArray(op.Srcs) && op.Srcs.length > 0 || Array.isArray(op.CellUpdates) && op.CellUpdates.length > 0);\nif (hasAvPayload(op)) throw new Error(`operation ${op.Action} is an attribute-view op; route it through the av contract`);","typeGuard":"const isBlockOperation = (op) => !!op && (op.Srcs == null || op.Srcs.length === 0) && (!op.CellUpdates || op.CellUpdates.length === 0);","tryCatchPattern":"try {\n  return convertOperationContract(op);\n} catch (e) {\n  if (String(e?.message ?? e?.msg).includes('unexpected attribute view payload')) {\n    return convertAvOperation(op); // route av ops to their own converter\n  }\n  throw e;\n}","preventionTips":["Keep block and attribute-view operations in separate transactions","Never populate Srcs/CellUpdates on block actions like 'insert'/'update'","Assert operation kind (isBlockOperation) before contract conversion in shared helpers"],"tags":["transactions","attribute-view","siyuan","contract"],"backgroundTag":"unexpected-response-shape","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"}