{"record":{"id":"2fa3d174f9876488","repo":"siyuan-note/siyuan","slug":"invalid-card-cover-position-v-v","errorCode":null,"errorMessage":"invalid card cover position [%v, %v]","messagePattern":"invalid card cover position \\[(.+?), (.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view.go","lineNumber":2070,"sourceCode":"\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\n\t}\n\tif av.LayoutTypeGallery != view.LayoutType && av.LayoutTypeKanban != view.LayoutType {\n\t\treturn av.ErrWrongLayoutType\n\t}\n\tvar source string","sourceCodeStart":2052,"sourceCodeEnd":2088,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view.go#L2052-L2088","documentation":"The cover position X/Y coordinates must be finite numbers in the inclusive range 0..100 (percent). NaN, +/-Inf, negative values, or values above 100 raise this error. It keeps the stored cover focal point a valid percentage.","triggerScenarios":"doSetAttrViewCardCoverPosition with data.position.x or .y being NaN/Infinity (e.g. from a JS division by zero) or outside 0-100, such as -5 or 150.","commonSituations":"Drag-position UI math producing NaN when element dimensions are zero; percent-to-pixel conversion bugs; clients passing raw pixel offsets instead of percentages.","solutions":["Clamp x and y into [0, 100] before building the operation (Math.min(100, Math.max(0, value))).","Guard against NaN/Infinity from division: check the denominator before computing the percentage.","Convert pixel offsets to percentages of the image size before sending."],"exampleFix":"// before\nconst x = offsetX / width * 100; // width may be 0 -> NaN\n// after\nconst x = width > 0 ? Math.min(100, Math.max(0, offsetX / width * 100)) : 50;","handlingStrategy":"validation","validationCode":"const clampPct = (n) => Math.min(100, Math.max(0, n));\nif (!Number.isFinite(position.x) || !Number.isFinite(position.y)) throw new Error(\"position must be finite\");","typeGuard":"const isPercent = (v: unknown): v is number => typeof v === \"number\" && Number.isFinite(v) && v >= 0 && v <= 100;","tryCatchPattern":"try { await setCardCoverPosition(...); } catch (e) { if (String(e).includes(\"invalid card cover position\")) retryWithClampedPosition(); else throw e; }","preventionTips":["Clamp x/y to [0,100] after any percentage math","Guard divisions against zero denominators to avoid NaN","Convert pixel coordinates to percentages before sending"],"tags":["attribute-view","card-cover","range-validation"],"backgroundTag":"value-out-of-range","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"}