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

  1. Use the exact token string returned by the stage step (stageSYImport) — pass it through verbatim, without trimming or re-encoding.
  2. Validate client-side that the token is 32 chars of [A-Za-z0-9] before issuing the claim request.
  3. 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

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


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