siyuan-note/siyuan · warning

pending edits during asset scan; retry the request

Error message

pending edits during asset scan; retry the request

What it means

Before applying a relink plan, the runner acquires the flush lock and checks the pending transaction queue (txQueueSize). If there are uncommitted document edits queued, applying renames now could race with the write queue and corrupt references, so the operation aborts and asks the client to retry once edits are flushed.

Solutions

  1. Retry the relink after a short delay so the transaction queue drains
  2. Ensure documents are saved/flushed (pause editing or commit pending edits) before running relink
  3. Schedule relink jobs during idle periods or stop the editor/other clients first
  4. Add client-side retry with backoff on this specific error message

Example fix

// before
_, err := model.RelinkAssets(ctx, mappings, false)
// after
var result apicontract.AssetReferencesData
for i := 0; i < 5; i++ {
	result, err = model.RelinkAssets(ctx, mappings, false)
	if err == nil || !strings.Contains(fmt.Sprint(err), "pending edits") { break }
	time.Sleep(2 * time.Second)
}
Defensive patterns

Strategy: retry

Validate before calling

if model.TxQueueSize() > 0 { waitUntilQueueEmpty(timeout) } // expose or check via API before calling

Try / catch

for i := 0; i < maxRetries; i++ {
	result, err := model.RelinkAssets(ctx, mappings, dryRun)
	if err == nil || !strings.Contains(fmt.Sprint(err), "pending edits") { return result, err }
	time.Sleep(backoff(i))
}

Prevention

When it happens

Trigger: Calling RelinkAssets/RelinkAssetWithContext while the user (or another client) is actively editing documents and the async transaction queue is non-empty (txQueueSize() > 0) at apply time.

Common situations: Triggering a batch relink via API immediately after heavy editing; automated scripts relinking while the editor is open and auto-saving; concurrent websocket clients producing ongoing transactions.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/asset_relink_batch.go:177

		case <-p.ctx.Done():
			return p.ctx.Err()
		case <-time.After(10 * time.Millisecond):
		}
	}
	// 扫描期间允许编辑;保存前在事务锁内校验文件与目录快照,新增引用也会触发重试。
	if err := p.scan(); err != nil {
		return err
	}
	p.finishPreflight()
	if p.result.DryRun || p.lookup {
		return nil
	}
	if err := lockAssetRelink(p.ctx, &flushLock); err != nil {
		return err
	}
	defer flushLock.Unlock()
	if txQueueSize() > 0 {
		return errors.New("pending edits during asset scan; retry the request")
	}
	return p.apply()
}

func (p *assetRelinkPlan) initialize() {
	if p.ctx == nil {
		p.ctx = context.Background()
	}
	if p.inventory == nil {
		p.inventory = map[string]assetRelinkStamp{}
	}
	if p.items == nil {
		rule := p.assetRelinker
		p.items = []*assetRelinkItem{{input: apicontract.AssetRelinkMapping{OldPath: rule.oldPath, NewPath: rule.newPath}, rule: rule}}
		p.assetRelinker = &assetRelinker{routes: map[string]*assetRelinker{rule.oldPath: rule}, collectOnly: true, result: rule.result}
	}
	if p.matcher == nil {
		patterns := []string{}

View on GitHub (pinned to 9f775e8a12)