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 "", errView on GitHub (pinned to 251596fc0d)
Solutions
- Make the caller consistent: pass the same boxID that appears in the URL, or strip the `?box=` query before calling.
- If the URL's box is authoritative, call GetAssetAbsPathInBox with an empty boxID and let the query param drive resolution.
- If the caller's box is authoritative, strip the query string via model.AssetPathWithoutQuery before resolving.
- 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
- Decide upfront whether the caller or the URL is authoritative for the box and be consistent.
- When copying/pasting across notebooks, rewrite or strip the ?box= param to match the destination.
- Avoid building asset URLs by concatenating a stale ?box= param.
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
- [%s] is not a box id
- assetPath is required for analyze
- only local assets/... images are supported
- assetPath is not an image referenced by the document
- asset path is required
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/9ab49abfc0b9ff62.
Report an issue: GitHub.