siyuan-note/siyuan · error

at least one asset path or mapping is required

Error message

at least one asset path or mapping is required

What it means

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.

Solutions

  1. Check the input list length before calling and return a client-side validation message for empty input
  2. Populate at least one mapping with a non-empty OldPath
  3. If filtering may remove all entries, skip the API call entirely when the filtered list is empty

Example fix

// before
result, err := model.RelinkAssets(ctx, filtered, dryRun) // filtered may be empty
// after
if len(filtered) == 0 {
	return nil, errors.New("no asset mappings to relink")
}
result, err := model.RelinkAssets(ctx, filtered, dryRun)
Defensive patterns

Strategy: validation

Validate before calling

if len(mappings) == 0 { return errors.New("provide at least one asset mapping") }

Type guard

if mappings == nil || len(mappings) == 0 { return false }

Prevention

When it happens

Trigger: 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.

Common situations: UI sends no selected assets; a script filters mappings and passes an empty array; API integration bug that never populates the paths parameter.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/1595a8cd0402a333. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/asset_relink_batch.go:70

func FindAssetReferencesBatch(ctx context.Context, paths []string) (apicontract.AssetReferencesData, error) {
	mappings := make([]apicontract.AssetRelinkMapping, len(paths))
	for i, path := range paths {
		mappings[i].OldPath = path
	}
	return runAssetRelinks(ctx, mappings, true, true, true)
}

func RelinkAssets(ctx context.Context, mappings []apicontract.AssetRelinkMapping, dryRun bool) (apicontract.AssetReferencesData, error) {
	return runAssetRelinks(ctx, mappings, dryRun, false, true)
}

func newAssetRelinkPlan(ctx context.Context, mappings []apicontract.AssetRelinkMapping, dryRun, lookup, batch bool) (*assetRelinkPlan, error) {
	p := &assetRelinkPlan{ctx: ctx, batch: batch, lookup: lookup, assetRelinker: &assetRelinker{
		collectOnly: true, routes: map[string]*assetRelinker{},
		result: apicontract.AssetReferencesData{References: []apicontract.AssetReference{}, SkippedNotebooks: []string{}, DryRun: dryRun},
	}}
	if len(mappings) == 0 {
		return p, errors.New("at least one asset path or mapping is required")
	}
	seen := map[string]bool{}
	var validationErr error
	for _, mapping := range mappings {
		rule, err := newAssetRelinker(mapping.OldPath, mapping.NewPath)
		if !lookup && mapping.NewPath == "" {
			err = errors.New("newPath is required")
		}
		item := &assetRelinkItem{input: mapping, rule: rule}
		p.items = append(p.items, item)
		if rule.oldPath != "" {
			if seen[rule.oldPath] {
				validationErr = fmt.Errorf("duplicate oldPath: %s", mapping.OldPath)
			}
			seen[rule.oldPath] = true
		}
		if err != nil {
			item.inputError = true

View on GitHub (pinned to 9f775e8a12)