{"record":{"id":"c29a68e098f1eb32","repo":"siyuan-note/siyuan","slug":"invalid-card-cover-image","errorCode":null,"errorMessage":"invalid card cover image","messagePattern":"invalid card cover image","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/attribute_view.go","lineNumber":1919,"sourceCode":"\t}\n\treturn\n}\n\nfunc setAttrViewCardCoverPosition(operation *Operation) (err error) {\n\tdataJSON, err := json.Marshal(operation.Data)\n\tif nil != err {\n\t\treturn\n\t}\n\tvar data setAttrViewCardCoverPositionData\n\tif err = json.Unmarshal(dataJSON, &data); nil != err {\n\t\treturn\n\t}\n\tif !av.IsValidCardCoverSource(data.Source) {\n\t\treturn fmt.Errorf(\"invalid card cover source [%s]\", data.Source)\n\t}\n\tif nil != data.Position {\n\t\tif \"\" == data.Position.Image || 32*1024 < len(data.Position.Image) {\n\t\t\treturn errors.New(\"invalid card cover image\")\n\t\t}\n\t\tif math.IsNaN(data.Position.X) || math.IsInf(data.Position.X, 0) ||\n\t\t\tmath.IsNaN(data.Position.Y) || math.IsInf(data.Position.Y, 0) ||\n\t\t\tdata.Position.X < 0 || 100 < data.Position.X || data.Position.Y < 0 || 100 < data.Position.Y {\n\t\t\treturn fmt.Errorf(\"invalid card cover position [%v, %v]\", data.Position.X, data.Position.Y)\n\t\t}\n\t}\n\n\tattrView, err := av.ParseAttributeView(operation.AvID)\n\tif nil != err {\n\t\treturn\n\t}\n\tif nil == attrView.GetBlockValue(operation.RowID) {\n\t\treturn fmt.Errorf(\"attribute view item [%s] not found\", operation.RowID)\n\t}\n\tview, err := getAttrViewViewByBlockID(attrView, operation.BlockID)\n\tif nil != err {\n\t\treturn","sourceCodeStart":1901,"sourceCodeEnd":1937,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/attribute_view.go#L1901-L1937","documentation":"Returned by setAttrViewCardCoverPosition (attribute_view.go:1919) when data.Position is non-nil but its Image field is empty or exceeds 32*1024 bytes (32 KiB). The Image field holds the cover image payload (a data URL / base64 used as a cache key for the position). The guard runs only when a position object is present; sending position = null is valid and clears the stored position.","triggerScenarios":"Frontend sends a non-null position with an empty Image string, or with an Image string longer than 32 KiB (a large base64-encoded cover). The 32 KiB cap exists because the image is stored as a position-cache key in av.CardCoverPositions, not as the actual asset.","commonSituations":"Plugin embeds the full image base64 instead of a short cache key; UI sends an empty Image because the cover URL was used instead; cover image changed and the stale payload was not refreshed, blowing past the limit.","solutions":["Send a short, stable image identifier (e.g. the asset URL/hash, not the full base64) under 32 KiB in position.image, or omit the position object to clear it.","If you only want to set X/Y focus, pass the same image reference the renderer uses as the cache key, trimmed to well under 32 KiB.","Send position = null when clearing the focal point rather than an object with an empty Image."],"exampleFix":"// before: embedding the full base64 cover\ntransaction({ op: \"setAttrViewCardCoverPosition\", data: { source, position: { image: fullBase64, x, y } } })\n\n// after: use a short cache key (the asset URL/hash)\ntransaction({ op: \"setAttrViewCardCoverPosition\", data: { source, position: { image: coverURL, x, y } } })","handlingStrategy":"validation","validationCode":"// image must be a short cache key, not full base64; cap at 32 KiB.\nif (position && (position.image === '' || position.image.length > 32 * 1024)) {\n  throw new Error('invalid card cover image')\n}","typeGuard":"function isValidCoverImage(position: unknown): boolean {\n  if (position == null) return true // null clears the position\n  if (typeof position !== 'object') return false\n  const img = (position as any).image\n  return typeof img === 'string' && img.length > 0 && img.length <= 32 * 1024\n}","tryCatchPattern":null,"preventionTips":["Send a short stable image reference (URL/hash) under 32 KiB, not the full base64.","Pass position = null to clear the focal point instead of an object with empty image.","Refresh the image key when the cover changes so stale large payloads are not replayed."],"tags":["attribute-view","av","gallery","kanban","card","cover","validation","size-limit","transaction"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}