siyuan-note/siyuan · error
box and dataPath cannot be used together
Error message
box and dataPath cannot be used together
What it means
resolveAssetRequestPath serves /assets/ requests in two mutually exclusive ways: by box ID (resolve within a notebook) or by an explicit data path (resolve relative to the workspace data directory). Passing both at once is ambiguous, so the handler rejects the request outright before doing any file resolution.
Source
Thrown at kernel/server/serve.go:986
}
return gulu.File.IsSubPath(filepath.Join(util.DataDir, requestBoxID, "assets"), assetAbsPath)
}
if model.IsEncryptedAssetPath(assetAbsPath) {
return false
}
dataRelativePath, err := filepath.Rel(util.DataDir, assetAbsPath)
if err != nil {
return false
}
_, validatedAbsPath, err := model.ResolveDataAssetPath(filepath.ToSlash(dataRelativePath))
return err == nil && filepath.Clean(validatedAbsPath) == filepath.Clean(assetAbsPath)
}
func resolveAssetRequestPath(cleanPath, boxID, dataPath string) (string, error) {
if dataPath != "" {
if boxID != "" {
return "", errors.New("box and dataPath cannot be used together")
}
dataRelativePath, assetAbsPath, err := model.ResolveDataAssetPath(dataPath)
if err != nil {
return "", err
}
assetPath, _, ok := model.AssetPathFromDataRelativePath(dataRelativePath)
if !ok || assetPath != cleanPath {
return "", fmt.Errorf("asset path [%s] does not match data path [%s]", cleanPath, dataPath)
}
return assetAbsPath, nil
}
if boxID != "" {
return model.GetAssetAbsPathInBox(cleanPath, boxID)
}
return model.GetAssetAbsPath(cleanPath)
}
func serveAssets(ginServer *gin.Engine) {View on GitHub (pinned to 8641553a1f)
Solutions
- Remove the `box` query parameter from the request URL and keep only `dataPath`
- Or remove `dataPath` and keep only `box` when the asset lives inside a specific notebook
- Fix the client code that constructs the URL so it chooses exactly one resolution strategy
Example fix
// before GET /assets/foo.png?box=20240101120000-abc123&dataPath=assets/foo.png // after GET /assets/foo.png?box=20240101120000-abc123
Defensive patterns
Strategy: validation
Validate before calling
// build the asset URL with exactly one resolution strategy
const url = dataPath
? `/assets/${name}?dataPath=${encodeURIComponent(dataPath)}`
: `/assets/${name}?box=${boxID}`; Try / catch
try {
const res = await fetch(url);
if (res.status === 400 && (await res.text()).includes("box and dataPath")) {
// strip one param and retry
}
} catch (e) { /* network */ } Prevention
- Never append both `box` and `dataPath` query parameters
- Centralize asset URL construction in one helper
- Prefer dataPath-only URLs when the asset is workspace-wide
When it happens
Trigger: An HTTP request to the asset endpoint that supplies both the `box` query parameter and the `dataPath` query parameter; any code path calling resolveAssetRequestPath(cleanPath, boxID, dataPath) with both non-empty strings (kernel/server/serve.go:986).
Common situations: A client (script, plugin, third-party WebDAV/HTTP client) built an asset URL by blindly appending every known parameter; a bookmarked URL from an older SiYuan version that used box now also carries dataPath after a frontend change.
Related errors
- asset path [%s] does not match data path [%s]
- list document images failed: %w
- assetPath is not an image referenced by the document
- nil response
- response too large
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/5207dcbac3fa9695.
Report an issue: GitHub.