siyuan-note/siyuan · error

Query asset failed [%s]

Error message

Query asset failed [%s]

What it means

Thrown by GetAssetAbsPathInBox (kernel/model/assets.go:1119) in the encrypted-box branch. When boxID is an encrypted notebook and the asset is not found under `<boxID>/assets/`, the resolver does NOT fall back to global `data/assets/` (encrypted boxes are resource islands), so it returns this localized not-found error. Conf.Language(12) renders as `Query asset failed [%s]`.

Source

Thrown at kernel/model/assets.go:1119

			if !gulu.File.IsSubPath(util.WorkspaceDir, realP) {
				return "", fmt.Errorf("symlink [%s] resolves outside workspace: [%s]", p, realP)
			}
			// 验证解析后的路径仍在 <boxID>/assets/ 或全局 data/assets/ 下
			expectedPrefix := filepath.Join(util.DataDir, "assets")
			if boxID != "" {
				expectedPrefix = filepath.Join(util.DataDir, boxID, "assets")
			}
			if !gulu.File.IsSubPath(expectedPrefix, realP) {
				return "", fmt.Errorf("symlink [%s] resolves outside assets directory: [%s]", p, realP)
			}
		}
		return p, nil
	}
	// 非加密 box 的资源可能回退到全局 data/assets(兼容旧笔记本结构)
	if !IsEncryptedBox(boxID) {
		return GetAssetAbsPathWithOpt(relativePath, false)
	}
	return "", fmt.Errorf(Conf.Language(12), relativePath)
}

// GetAssetAbsPathWithOpt 与 GetAssetAbsPath 一致,但可通过 includeEncrypted 控制是否遍历加密 box。
// serveAssets 传 true(下游 serveEncryptedAsset 会按锁定状态 fail-closed),其他调用方传 false(安全跳过)。
func GetAssetAbsPathWithOpt(relativePath string, includeEncrypted bool) (string, error) {
	relativePath = strings.TrimSpace(relativePath)
	if idx := strings.Index(relativePath, "?"); idx >= 0 {
		relativePath = relativePath[:idx]
	}

	absPath, err := getAssetAbsPath(relativePath, includeEncrypted)
	if err == nil && absPath != "" {
		return absPath, nil
	}

	if err != nil {
		return "", err
	}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Confirm the asset actually exists under `<DataDir>/<boxID>/assets/`.
  2. If the asset lives in global `data/assets/` or another notebook, copy it into the encrypted box's assets directory (encrypted boxes cannot read external assets).
  3. If the box is not meant to be encrypted, verify IsEncryptedBox(boxID) returns what you expect (check the box's encryption/lock state).
  4. Re-run the encrypted-box sync/decryption so the local assets directory is complete, then retry.
Defensive patterns

Strategy: validation

Validate before calling

// Before resolving in an encrypted box, confirm the asset exists there.
if model.IsEncryptedBox(boxID) {
    p := filepath.Join(util.DataDir, boxID, filepath.FromSlash(rel))
    if !gulu.File.IsExist(p) {
        return fmt.Errorf("asset %s not present in encrypted box %s; copy it in first", rel, boxID)
    }
}

Try / catch

if _, err := model.GetAssetAbsPathInBox(ref, box); err != nil {
    if model.IsEncryptedBox(box) && strings.Contains(err.Error(), "Query asset failed") {
        // encrypted-box isolation: no global fallback; copy the asset into the box, then retry
    }
}

Prevention

When it happens

Trigger: Calling GetAssetAbsPathInBox(path, boxID) where boxID is an encrypted notebook and the referenced asset does not exist under that box's own assets directory. The asset may exist globally or in another notebook, but encrypted-box isolation forbids the fallback.

Common situations: An asset reference inside an encrypted notebook points to a global/notebook-shared asset that was never copied into the encrypted box; the encrypted box's assets directory is incomplete after a partial sync; a document was moved into an encrypted notebook without migrating its assets.

Related errors


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