siyuan-note/siyuan · warning

SQL search mode does not support the replace operation…

Error message

SQL search mode does not support the replace operation, please use another search method

What it means

FindReplaceInBox explicitly rejects SQL search mode (method == 2): replacing text found by a raw SQL query is not supported because SQL matches cannot be reliably mapped back to editable text ranges. The error message is i18n language string 132 telling the user to choose another search method.

Solutions

  1. Use method 0 (text), 1 (query syntax) or 3 (regex) for the replace operation
  2. Run the SQL query separately to collect IDs, then replace per block with a supported method
  3. Update the calling UI to disable or remap SQL mode when a replacement is requested

Example fix

// before
err := model.FindReplace(k, r, types, ids, paths, boxes, method /* 2 = SQL */, replaceTypes)
// after
if method == 2 {
    method = 0 // fall back to text search for replace
}
err := model.FindReplace(k, r, types, ids, paths, boxes, method, replaceTypes)
Defensive patterns

Strategy: validation

Validate before calling

if (method === 2 && replacement) { throw new Error('SQL mode cannot replace; use text/query/regex') }

Try / catch

try { await api.findReplace(k, r, { method }) } catch (e) { if (String(e).includes('replace operation')) { notifyUserToChangeSearchMethod(); } else throw e }

Prevention

When it happens

Trigger: Calling FindReplace or FindReplaceInBox with method=2 (SQL) and a non-empty replacement — the check fires before any search runs.

Common situations: Scripts or UI automation reusing the last search method (SQL) when issuing a replace; users switching from search-only to replace without changing the method dropdown.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/search.go:636

	}
	boxIDs = gulu.Str.RemoveDuplicatedElem(boxIDs)
	boxNames := Conf.BoxNames(boxIDs)
	for _, b := range blocks {
		name := boxNames[b.Box]
		b.HPath = util.EscapeHTML(name) + b.HPath
	}
}

func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids []string, paths, boxes []string, types, subTypes map[string]bool, method, orderBy, groupBy int) (err error) {
	// orderBy 和 groupBy 仅用于兼容现有调用,替换目标不依赖搜索结果的展示顺序和分组方式。
	return FindReplaceInBox(keyword, replacement, replaceTypes, ids, paths, boxes, types, subTypes, method, "")
}

// FindReplaceInBox 与 FindReplace 一致,但按 boxID 路由到加密 db 或全局 db。
func FindReplaceInBox(keyword, replacement string, replaceTypes map[string]bool, ids []string, paths, boxes []string, types, subTypes map[string]bool, method int, boxID string) (err error) {
	// method:0:文本,1:查询语法,2:SQL,3:正则表达式
	if 2 == method {
		err = errors.New(Conf.Language(132))
		return
	}

	if 1 == method {
		// 将查询语法等价于关键字,因为 keyword 参数已经是结果关键字了
		// Find and replace supports query syntax https://github.com/siyuan-note/siyuan/issues/14937
		method = 0
	}

	// No longer trim spaces for the keyword and replacement https://github.com/siyuan-note/siyuan/issues/9229
	if keyword == replacement {
		return
	}

	r, _ := regexp.Compile(keyword)
	escapedKey := util.EscapeHTML(keyword)
	escapedKey = strings.ReplaceAll(escapedKey, """, """)
	escapedKey = strings.ReplaceAll(escapedKey, "'", "'")

View on GitHub (pinned to 9f775e8a12)