siyuan-note/siyuan · warning

local asset path is not allowed

Error message

local asset path is not allowed

What it means

When inserting assets from an HTML context (validateHTMLPath is true, i.e. InsertHTMLLocalAssets), each absolute path is screened with util.IsSensitivePath and EncryptedRawPathBoxID. Paths deemed sensitive (e.g. pointing into system/config locations) or located inside an encrypted notebook's raw storage are rejected with "local asset path is not allowed" and skipped as a per-file failure.

Source

Thrown at kernel/model/upload.go:174

			return
		}
		boxID = bt.BoxID
		docDirLocalPath := filepath.Join(util.DataDir, boxID, path.Dir(bt.Path))
		assetsDirPath = getAssetsDir(filepath.Join(util.DataDir, boxID), docDirLocalPath)
	}
	if !gulu.File.IsExist(assetsDirPath) {
		if err = os.MkdirAll(assetsDirPath, 0755); err != nil {
			return
		}
	}

	for index, assetAbsPath := range assetAbsPaths {
		if strings.HasPrefix(strings.ToLower(assetAbsPath), "file://") {
			assetAbsPath = util.FileURLToLocalPath(assetAbsPath)
		}
		baseName := filepath.Base(assetAbsPath)
		if validateHTMLPath && (util.IsSensitivePath(assetAbsPath) || EncryptedRawPathBoxID(assetAbsPath) != "") {
			recordAssetUploadFailure(&failedFiles, index, baseName, errors.New("local asset path is not allowed"))
			continue
		}
		fName := baseName
		fName = util.FilterUploadFileName(fName)
		ext := filepath.Ext(fName)
		fName = strings.TrimSuffix(fName, ext)
		ext = strings.ToLower(ext)
		fName += ext
		if gulu.File.IsDir(assetAbsPath) || !isUpload {
			if !strings.HasPrefix(assetAbsPath, "\\\\") {
				assetAbsPath = "file://" + assetAbsPath
			}
			recordAssetUploadSuccess(succMap, &succFiles, index, baseName, assetAbsPath)
			continue
		}

		if gulu.File.IsSubPath(assetsDirPath, assetAbsPath) {
			// 已经位于 assets 目录下的资源文件不处理

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Copy the file to a normal, non-sensitive location (e.g. a temp directory) and insert from there.
  2. Check the path with util.IsSensitivePath before calling; skip or relocate files inside encrypted notebook raw storage.
  3. Use the standard Upload API (multipart form) instead of local-path insertion for files originating from protected locations.

Example fix

// before
model.InsertHTMLLocalAssets(docID, []string{"/workspace/data/encrypted-box/raw/image.png"})
// after
tmp := filepath.Join(os.TempDir(), "image.png")
copyFile(tmp, "/workspace/data/encrypted-box/raw/image.png")
model.InsertHTMLLocalAssets(docID, []string{tmp})
Defensive patterns

Strategy: validation

Validate before calling

func insertableHTMLAsset(p string) bool {
    if strings.HasPrefix(strings.ToLower(p), "file://") {
        p = util.FileURLToLocalPath(p)
    }
    return !util.IsSensitivePath(p) && model.EncryptedRawPathBoxID(p) == ""
}

Try / catch

_, _, failed, err := model.InsertHTMLLocalAssets(docID, paths)
for _, f := range failed {
    if f.Error == "local asset path is not allowed" {
        // copy to temp and retry that single file
        tmp := copyToTemp(paths[f.Index])
        model.InsertHTMLLocalAssets(docID, []string{tmp})
    }
}

Prevention

When it happens

Trigger: Calling InsertHTMLLocalAssets with a file:// URL or absolute path that resolves to a sensitive path (per util.IsSensitivePath), or a path inside an encrypted notebook's raw data area; HTML paste/drop flows referencing such locations.

Common situations: Pasting HTML whose embedded images point at restricted local paths; browser-clipper or automation flows supplying paths under the workspace's protected directories; attempting to re-import a file that lives inside an encrypted box's raw storage.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/e364334ae1c7306d. Report an issue: GitHub.