siyuan-note/siyuan · error

box mismatch: caller specified

Error message

box mismatch: caller specified [%s] but URL has [%s]

What it means

assetPathAndBox parses an optional ?box=<id> query parameter from an asset path and reconciles it with the boxID the caller passed explicitly. When the caller supplied a non-empty boxID that differs from the box ID in the URL query, this error is returned to avoid silently resolving the asset from the wrong notebook.

Solutions

  1. Make the box IDs consistent: either drop the caller-provided boxID (pass "") and let the URL's ?box= parameter win, or strip the ?box= query from the path before calling
  2. Update the stored asset link so its ?box= parameter matches the notebook it lives in
  3. If the asset truly belongs to another notebook, call the resolver with that notebook's boxID and no conflicting query parameter

Example fix

// before: conflict between caller box and URL query
model.GetAssetAbsPathInBox("assets/img.png?box=20240101111111-aaaa", "20250101111222-bbbb")
// after: strip the query or align the box
model.GetAssetAbsPathInBox("assets/img.png", "20250101111222-bbbb")
Defensive patterns

Strategy: validation

Validate before calling

u, _ := url.Parse(assetRef)
qBox := u.Query().Get("box")
if qBox != "" && callerBoxID != "" && qBox != callerBoxID {
	// reconcile before calling: pass one source of truth only
}

Try / catch

abs, err := model.GetAssetAbsPathInBox(ref, boxID)
if err != nil && strings.Contains(err.Error(), "box mismatch") {
	// strip the query and retry with the caller's box
	clean := strings.SplitN(ref, "?", 2)[0]
	abs, err = model.GetAssetAbsPathInBox(clean, boxID)
}

Prevention

When it happens

Trigger: Calling GetAssetAbsPathInBox (or functions routed through assetPathAndBox such as assetReferenceExists, AcquireExportArtifactLease, GetMobileExportName) with e.g. path="assets/img.png?box=20240101111111-aaaa" while also passing boxID="20250101111222-bbbb".

Common situations: Copying an asset URL (which embeds its source box) from one document into code that resolves it within a different notebook's context; export code passing the current box while the stored link was created in another notebook; stale hard-coded URLs after merging notebooks.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/assets.go:1202

func IsHTMLAssetIFrameSrc(assetPath string) bool {
	parsed, err := url.Parse(assetPath)
	if err != nil || !strings.EqualFold(parsed.Query().Get("iframe"), "true") {
		return false
	}
	return IsLocalHTMLAssetPath(assetPath)
}

func assetPathAndBox(relativePath, defaultBoxID string) (cleanPath, boxID string, err error) {
	relativePath = strings.TrimSpace(relativePath)
	boxID = defaultBoxID
	if idx := strings.Index(relativePath, "?"); idx >= 0 {
		query := relativePath[idx+1:]
		relativePath = relativePath[:idx]
		if values, parseErr := url.ParseQuery(query); parseErr == nil {
			if queryBoxID := strings.TrimSpace(values.Get("box")); queryBoxID != "" {
				if defaultBoxID != "" && defaultBoxID != queryBoxID {
					// 调用方指定了 boxID 但 URL 里是另一个 box:拒绝,防止解析到错误 box
					err = fmt.Errorf("box mismatch: caller specified [%s] but URL has [%s]", defaultBoxID, queryBoxID)
					return
				}
				boxID = queryBoxID
			}
		}
	}
	cleanPath = filepath.ToSlash(relativePath)
	return
}

// GetAssetAbsPathInBox 在指定 box 内解析资源绝对路径,不进行全局遍历。
// relativePath 必须以 assets/ 前缀开头,boxID 为空且路径没有 box 查询参数时只解析普通/全局资源,不遍历加密 box。
// 加密 box 直接从 <boxID>/assets/ 查找,不依赖后缀匹配。
func GetAssetAbsPathInBox(relativePath, boxID string) (string, error) {
	var err error
	relativePath, boxID, err = assetPathAndBox(relativePath, boxID)
	if err != nil {
		return "", err

View on GitHub (pinned to 9f775e8a12)