siyuan-note/siyuan · error
invalid import token
Error message
invalid import token
What it means
claimStagedSYImport validates the opaque token returned by the staging step before renaming the staged .sy zip out of the staging directory. isValidSYImportToken rejects tokens that are not well-formed (wrong charset/length), so a malformed or fabricated token yields 'invalid import token'.
Solutions
- Pass the exact token string returned by the stage/upload response, unmodified.
- Verify the token matches isValidSYImportToken's expected format before calling.
- If the original response was lost, restart the import to get a fresh token.
- Ensure the token is not wrapped in quotes or whitespace from copy/paste.
Example fix
// before
claim("../../etc/tmp-import.zip") // invalid
// after
token := stageResponse.data.token
if !isValidSYImportToken(token) { /* re-stage */ }
claim(token) Defensive patterns
Strategy: validation
Validate before calling
if (!/^[A-Za-z0-9_-]{6,64}$/.test(token)) throw new Error("malformed import token") Type guard
function isValidImportToken(t) { return typeof t === "string" && /^[A-Za-z0-9_-]+$/.test(t) } Try / catch
try { await claim(token) }
catch (e) { if (e.message === "invalid import token") restageAndRetry() else throw e } Prevention
- Store the token verbatim from the stage response; never re-encode it
- Use the stage+claim pair in one flow without manual copy/paste
- Regenerate the token after any failure instead of guessing
When it happens
Trigger: Calling the claim API with a token that was never issued, hand-crafted, truncated, or contains characters outside the allowed token alphabet (e.g. path separators — also a traversal guard).
Common situations: Client stores the token incorrectly (URL-encoded/decoded wrongly); retry after the token expired uses a stale/guessed value; attacker probes the endpoint with arbitrary strings.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- import path is not sub path of import dir
- 199
- Argon2id Iterations too low (minimum 3)
- asset path escapes data directory
- asset path escapes data directory
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/57c35b818f2b9ee6.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/import.go:227
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 9f775e8a12)