siyuan-note/siyuan · warning

Do not include symbols \ / : * ? " ' < > |

Error message

Do not include symbols \ / : * ? " ' < > |

What it means

Returned by RenameAsset (assets.go:1511) when the new asset name fails gulu.File.IsValidFilename after trimming and util.FilterUploadFileName. The message is the kernel i18n key 151 listing forbidden characters: backslash, slash, colon, asterisk, question mark, double-quote, single-quote, less-than, greater-than, pipe. The HTML-entity form (" ' < >) is used because the string is rendered in the UI.

Source

Thrown at kernel/model/assets.go:1511

	// 加密笔记本的资源磁盘文件名参与 AAD,重命名需要重新封装密文,当前不支持。
	if absPath, absErr := GetAssetAbsPathInBox(oldPath, ""); absErr == nil {
		if IsEncryptedAssetPath(absPath) {
			err = errors.New("renaming assets in encrypted notebooks is not supported")
			return
		}
	}

	newName = strings.TrimSpace(newName)
	newName = util.FilterUploadFileName(newName)
	if path.Base(oldCleanPath) == newName {
		return
	}
	if "" == newName {
		return
	}

	if !gulu.File.IsValidFilename(newName) {
		err = errors.New(Conf.Language(151))
		return
	}

	newName = util.AssetName(newName+filepath.Ext(oldCleanPath), ast.NewNodeID())
	parentDir := path.Dir(oldCleanPath)
	newPath = path.Join(parentDir, newName)
	oldAbsPath, getErr := GetAssetAbsPathInBox(oldPath, "")
	if getErr != nil {
		logging.LogErrorf("get asset [%s] abs path failed: %s", oldPath, getErr)
		return
	}
	newAbsPath := filepath.Join(filepath.Dir(oldAbsPath), newName)
	filelock.Lock(oldAbsPath)
	if err = os.Rename(oldAbsPath, newAbsPath); err != nil {
		if err = gulu.File.Copy(oldAbsPath, newAbsPath); err != nil {
			filelock.Unlock(oldAbsPath)
			logging.LogErrorf("copy asset [%s] failed: %s", oldAbsPath, err)
			return

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Strip or replace the forbidden characters (\ / : * ? < > | and both quote kinds) before calling RenameAsset.
  2. Run the candidate through gulu.File.IsValidFilename (or an equivalent regex) on the client side so the user gets immediate feedback.
  3. If the name is user-typed, reject the characters in the input field rather than after submission.

Example fix

// before: passing raw user input
model.RenameAsset(oldPath, userInput)

// after: validate client-side first
const forbidden = /[\\\/:*?"'<>|]/
if (forbidden.test(userInput)) {
    showMessage(window.siyuan.languages[151])
    return
}
model.RenameAsset(oldPath, userInput)
Defensive patterns

Strategy: validation

Validate before calling

// Reject forbidden filename characters before calling renameAsset.
const FORBIDDEN = /[\\\/:*?"'<>|]/
if (!newName || FORBIDDEN.test(newName)) {
  showMessage(window.siyuan.languages[151])
  return
}

Try / catch

// Validate client-side; the kernel will also reject, so surface the i18n tip.
try { await renameAsset(oldPath, newName) }
catch (e) { if (/symbols/i.test(e.message)) highlightBadChars(newName); else throw e }

Prevention

When it happens

Trigger: Calling /api/asset/renameAsset with a newName that contains any of \ / : * ? < > | or a quote, is empty after filtering, or otherwise violates the portable-filename rules enforced by gulu.File.IsValidFilename. The check runs after FilterUploadFileName strips some characters, so the residual name still carried a forbidden symbol.

Common situations: User pastes a name with trailing punctuation or a date containing colons (e.g. "img:2024.png"); name copied from a URL with slashes/queries; plugin submits a raw filename without sanitization; cross-platform name using Windows-illegal characters on a Linux host (the guard is OS-independent).

Related errors


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