siyuan-note/siyuan · error

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

Error message

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

What it means

Thrown by assetPathAndBox (kernel/model/assets.go:1059, used by GetAssetAbsPathInBox) when the caller supplies a non-empty defaultBoxID AND the asset relativePath contains a `?box=<id>` query parameter whose value differs from the caller's box. It prevents a URL from silently redirecting asset resolution to a different notebook than the caller intended.

Source

Thrown at kernel/model/assets.go:1059

	}
	if parsed.IsAbs() || parsed.Host != "" || !strings.HasPrefix(strings.TrimPrefix(parsed.Path, "/"), "assets/") {
		return false
	}
	ext := strings.ToLower(path.Ext(parsed.Path))
	return ext == ".html" || ext == ".htm"
}

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 251596fc0d)

Solutions

  1. Make the caller consistent: pass the same boxID that appears in the URL, or strip the `?box=` query before calling.
  2. If the URL's box is authoritative, call GetAssetAbsPathInBox with an empty boxID and let the query param drive resolution.
  3. If the caller's box is authoritative, strip the query string via model.AssetPathWithoutQuery before resolving.
  4. Audit the document/source that produced the mismatched box param — it may indicate a copy/paste bug.

Example fix

// before
abs, err := model.GetAssetAbsPathInBox(assetRef, tree.Box)
// where assetRef = "assets/img.png?box=20240101000000-aaaaaaa" but tree.Box = "20250202000000-bbbbbbb"

// after — URL box is authoritative
abs, err := model.GetAssetAbsPathInBox(assetRef, "")
// or — caller box is authoritative
abs, err := model.GetAssetAbsPathInBox(model.AssetPathWithoutQuery(assetRef), tree.Box)
Defensive patterns

Strategy: validation

Validate before calling

// Make box sourcing unambiguous before calling GetAssetAbsPathInBox.
// Option A: let the URL drive — pass empty box.
// Option B: let the caller drive — strip the query.
if callerBox != "" {
    clean := model.AssetPathWithoutQuery(assetRef)
    return model.GetAssetAbsPathInBox(clean, callerBox)
}
return model.GetAssetAbsPathInBox(assetRef, "")

Try / catch

if _, err := model.GetAssetAbsPathInBox(ref, box); err != nil && strings.Contains(err.Error(), "box mismatch") {
    // reconcile caller box vs URL box; pick one authoritative source and retry once
}

Prevention

When it happens

Trigger: Calling model.GetAssetAbsPathInBox(relativePath, boxID) where relativePath is something like `assets/file.png?box=20250101000000-aaaaaaa` and the boxID argument is a different valid notebook ID. This happens when a document from notebook A embeds an asset carrying notebook B's box query param and the caller (e.g. export.go, mcp/tools/image.go) scopes resolution to A.

Common situations: Cross-notebook copy/paste where the asset link retained the source notebook's `?box=` param; inconsistent caller code that mixes the block's box with a URL query built elsewhere; manually crafted asset URLs.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/9ab49abfc0b9ff62. Report an issue: GitHub.