{"record":{"id":"1595a8cd0402a333","repo":"siyuan-note/siyuan","slug":"at-least-one-asset-path-or-mapping-is-required","errorCode":null,"errorMessage":"at least one asset path or mapping is required","messagePattern":"at least one asset path or mapping is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_relink_batch.go","lineNumber":70,"sourceCode":"func FindAssetReferencesBatch(ctx context.Context, paths []string) (apicontract.AssetReferencesData, error) {\n\tmappings := make([]apicontract.AssetRelinkMapping, len(paths))\n\tfor i, path := range paths {\n\t\tmappings[i].OldPath = path\n\t}\n\treturn runAssetRelinks(ctx, mappings, true, true, true)\n}\n\nfunc RelinkAssets(ctx context.Context, mappings []apicontract.AssetRelinkMapping, dryRun bool) (apicontract.AssetReferencesData, error) {\n\treturn runAssetRelinks(ctx, mappings, dryRun, false, true)\n}\n\nfunc newAssetRelinkPlan(ctx context.Context, mappings []apicontract.AssetRelinkMapping, dryRun, lookup, batch bool) (*assetRelinkPlan, error) {\n\tp := &assetRelinkPlan{ctx: ctx, batch: batch, lookup: lookup, assetRelinker: &assetRelinker{\n\t\tcollectOnly: true, routes: map[string]*assetRelinker{},\n\t\tresult: apicontract.AssetReferencesData{References: []apicontract.AssetReference{}, SkippedNotebooks: []string{}, DryRun: dryRun},\n\t}}\n\tif len(mappings) == 0 {\n\t\treturn p, errors.New(\"at least one asset path or mapping is required\")\n\t}\n\tseen := map[string]bool{}\n\tvar validationErr error\n\tfor _, mapping := range mappings {\n\t\trule, err := newAssetRelinker(mapping.OldPath, mapping.NewPath)\n\t\tif !lookup && mapping.NewPath == \"\" {\n\t\t\terr = errors.New(\"newPath is required\")\n\t\t}\n\t\titem := &assetRelinkItem{input: mapping, rule: rule}\n\t\tp.items = append(p.items, item)\n\t\tif rule.oldPath != \"\" {\n\t\t\tif seen[rule.oldPath] {\n\t\t\t\tvalidationErr = fmt.Errorf(\"duplicate oldPath: %s\", mapping.OldPath)\n\t\t\t}\n\t\t\tseen[rule.oldPath] = true\n\t\t}\n\t\tif err != nil {\n\t\t\titem.inputError = true","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/asset_relink_batch.go#L52-L88","documentation":"newAssetRelinkPlan requires at least one mapping (an asset path to find or an old/new pair to relink). An empty mappings slice cannot form a plan, so the constructor fails immediately with this error before any scanning.","triggerScenarios":"Calling FindAssetReferencesBatch with an empty paths slice, RelinkAssets with an empty mappings slice, or an API handler that drops all entries after filtering, reaching newAssetRelinkPlan with len(mappings)==0.","commonSituations":"UI sends no selected assets; a script filters mappings and passes an empty array; API integration bug that never populates the paths parameter.","solutions":["Check the input list length before calling and return a client-side validation message for empty input","Populate at least one mapping with a non-empty OldPath","If filtering may remove all entries, skip the API call entirely when the filtered list is empty"],"exampleFix":"// before\nresult, err := model.RelinkAssets(ctx, filtered, dryRun) // filtered may be empty\n// after\nif len(filtered) == 0 {\n\treturn nil, errors.New(\"no asset mappings to relink\")\n}\nresult, err := model.RelinkAssets(ctx, filtered, dryRun)","handlingStrategy":"validation","validationCode":"if len(mappings) == 0 { return errors.New(\"provide at least one asset mapping\") }","typeGuard":"if mappings == nil || len(mappings) == 0 { return false }","tryCatchPattern":null,"preventionTips":["Validate request payload emptiness at the API layer before invoking the model","Skip the call entirely when a filtered list becomes empty","Write tests covering empty-input cases for batch APIs"],"tags":["go","validation","empty-input"],"backgroundTag":"missing-required-argument","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"}