{"record":{"id":"721fcc2ce9b58970","repo":"siyuan-note/siyuan","slug":"invalid-rich-clipboard-asset-index-d","errorCode":null,"errorMessage":"invalid rich clipboard asset index [%d]","messagePattern":"invalid rich clipboard asset index \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/clipboard.go","lineNumber":83,"sourceCode":"\nfunc PrepareRichClipboardAssets(assets []RichClipboardAsset) (ret *RichClipboardPrepared, err error) {\n\tif len(assets) < 1 || 1024 < len(assets) {\n\t\treturn nil, fmt.Errorf(\"invalid rich clipboard asset count [%d]\", len(assets))\n\t}\n\n\tbatch := util.RandString(24)\n\tgroups := map[string]struct{}{}\n\tcopied := map[string]string{}\n\tret = &RichClipboardPrepared{Batch: batch}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tcleanupRichClipboardGroups(batch, groups)\n\t\t}\n\t}()\n\n\tfor _, asset := range assets {\n\t\tif asset.Index < 0 {\n\t\t\treturn nil, fmt.Errorf(\"invalid rich clipboard asset index [%d]\", asset.Index)\n\t\t}\n\n\t\text := strings.ToLower(filepath.Ext(AssetPathWithoutQuery(asset.Path)))\n\t\tif _, ok := richClipboardImageExts[ext]; !ok {\n\t\t\treturn nil, fmt.Errorf(\"unsupported rich clipboard image extension [%s]\", ext)\n\t\t}\n\n\t\tabsPath, resolveErr := GetAssetAbsPathInBox(asset.Path, asset.Box)\n\t\tif resolveErr != nil {\n\t\t\treturn nil, resolveErr\n\t\t}\n\n\t\tdestPath, ok := copied[absPath]\n\t\tif !ok {\n\t\t\tgroup := ExtractBoxIDFromAssetsPath(absPath)\n\t\t\tif group == \"\" {\n\t\t\t\tgroup = richClipboardGlobalGroup\n\t\t\t}","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/clipboard.go#L65-L101","documentation":"Returned by PrepareRichClipboardAssets when any asset in the input slice has an Index field less than zero. The Index denotes the positional slot of the image in the rich clipboard payload, so a negative value is treated as invalid input. The %d placeholder is filled with asset.Index.","triggerScenarios":"An asset struct is constructed with an uninitialized (zero is fine) or explicitly negative Index, typically from deserializing malformed JSON or from an off-by-one in frontend index calculation. The check runs inside the per-asset loop, after the count guard.","commonSituations":"Frontend assigns -1 as a sentinel for 'no index' instead of using 0; JSON parsing defaults; arithmetic bug that subtracts from a zero-based index producing -1.","solutions":["Ensure every asset.Index is >= 0 before sending; use 0-based indexing without negative sentinels.","Validate the JSON payload with a schema that enforces minimum 0 on the index field.","If a 'no position' meaning is needed, drop the asset from the array rather than using a negative index."],"exampleFix":"// before\nassets = append(assets, RichClipboardAsset{Index: -1, Path: p})\n\n// after\nassets = append(assets, RichClipboardAsset{Index: pos, Path: p}) // pos is always >= 0","handlingStrategy":"validation","validationCode":"// Reject negative indices before sending\nfor _, a := range assets {\n    if a.Index < 0 { /* drop or clamp to 0 */ }\n}","typeGuard":"func hasValidIndices(assets []RichClipboardAsset) bool {\n    for _, a := range assets { if a.Index < 0 { return false } }\n    return true\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"invalid rich clipboard asset index\") { sanitizeIndicesAndRetry() }","preventionTips":["Never use negative numbers as positional sentinels.","Validate the JSON schema with minimum: 0 on the index field.","Drop assets without a meaningful position rather than using -1."],"tags":["clipboard","assets","validation","input-validation"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}