siyuan-note/siyuan · error
invalid import token
Error message
invalid import token
What it means
Returned by claimStagedSYImport (import.go:258) when the provided SY-import token fails isValidSYImportToken: it must be exactly 32 characters of ASCII letters/digits ([A-Za-z0-9]). The two-phase .sy import (stage then claim) hands back an opaque token from stageSYImport; a malformed, truncated, or tampered token is refused before any filesystem rename occurs, to prevent path-injection or stale-token abuse.
Source
Thrown at kernel/api/import.go:258
return
}
for {
token = gulu.Rand.String(32)
_, statErr := os.Stat(stagedSYImportPath(token))
if os.IsNotExist(statErr) {
break
}
if statErr != nil {
return "", statErr
}
}
err = os.Rename(srcPath, stagedSYImportPath(token))
return
}
func claimStagedSYImport(token string) (path string, err error) {
if !isValidSYImportToken(token) {
return "", errors.New("invalid import token")
}
stagedSYImportLock.Lock()
defer stagedSYImportLock.Unlock()
cleanupStagedSYImports()
srcPath := stagedSYImportPath(token)
if _, err = os.Stat(srcPath); err != nil {
if os.IsNotExist(err) {
err = errors.New("import task not found or expired")
}
return "", err
}
path = filepath.Join(stagedSYImportDir(), token+"-importing.zip")
err = os.Rename(srcPath, path)
return
}
func cleanupStagedSYImports() {
entries, err := os.ReadDir(stagedSYImportDir())View on GitHub (pinned to 251596fc0d)
Solutions
- Use the exact token string returned by the stage step (stageSYImport) — pass it through verbatim, without trimming or re-encoding.
- Validate client-side that the token is 32 chars of [A-Za-z0-9] before issuing the claim request.
- If the token was lost or corrupted, re-stage the import to obtain a fresh token.
Defensive patterns
Strategy: validation
Validate before calling
// Validate the token matches the exact format the kernel expects
function isValidSYImportToken(t) {
return typeof t === 'string' && /^[A-Za-z0-9]{32}$/.test(t);
}
if (!isValidSYImportToken(token)) throw new Error('malformed import token'); Type guard
function isSYImportToken(t: unknown): t is string {
return typeof t === 'string' && /^[A-Za-z0-9]{32}$/.test(t);
} Try / catch
try { await claimImport(token); }
catch (e) { if (/invalid import token/.test(e.msg)) { token = await stageImport(data); await claimImport(token); } else throw e; } Prevention
- Pass the stage-returned token verbatim; never trim/encode it.
- Validate the 32-char alphanumeric shape client-side before claiming.
- Treat the token as opaque — don't reconstruct it from parts.
When it happens
Trigger: Calling the claim/finalize step of the SY import flow with a token that is not the 32-char alphanumeric string returned by stageSYImport (import.go:235). Passing a URL-decoded, base64, or hand-typed token. A frontend bug that truncated/whitespace-padded the token before the claim request.
Common situations: Frontend stores the token and accidentally trims/encodes it. A second claim attempt using a derived or remembered token. Replay across kernel restarts with an old token format. Manual testing with a placeholder like 'test' or 'token'.
Related errors
- import path is not sub path of import dir
- no file found
- invalid custom emoji URL
- Obsidian Vault path is unsafe
- Obsidian Vault path is unsafe: Vault root is a symbolic link
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/57c35b818f2b9ee6.
Report an issue: GitHub.