{"record":{"id":"3138002623c06240","repo":"siyuan-note/siyuan","slug":"import-task-not-found-or-expired","errorCode":null,"errorMessage":"import task not found or expired","messagePattern":"import task not found or expired","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/api/import.go","lineNumber":235,"sourceCode":"\t\tif statErr != nil {\n\t\t\treturn \"\", statErr\n\t\t}\n\t}\n\terr = os.Rename(srcPath, stagedSYImportPath(token))\n\treturn\n}\n\nfunc claimStagedSYImport(token string) (path string, err error) {\n\tif !isValidSYImportToken(token) {\n\t\treturn \"\", errors.New(\"invalid import token\")\n\t}\n\tstagedSYImportLock.Lock()\n\tdefer stagedSYImportLock.Unlock()\n\tcleanupStagedSYImports()\n\tsrcPath := stagedSYImportPath(token)\n\tif _, err = os.Stat(srcPath); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\terr = errors.New(\"import task not found or expired\")\n\t\t}\n\t\treturn \"\", err\n\t}\n\tpath = filepath.Join(stagedSYImportDir(), token+\"-importing.zip\")\n\terr = os.Rename(srcPath, path)\n\treturn\n}\n\nfunc cleanupStagedSYImports() {\n\tentries, err := os.ReadDir(stagedSYImportDir())\n\tif err != nil {\n\t\treturn\n\t}\n\tnow := time.Now()\n\tfor _, entry := range entries {\n\t\tname := entry.Name()\n\t\tif !strings.HasSuffix(name, \".zip\") || !isValidSYImportToken(strings.TrimSuffix(name, \".zip\")) {\n\t\t\tcontinue","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/import.go#L217-L253","documentation":"claimStagedSYImport looks up the staged zip at stagedSYImportPath(token); staged imports expire (cleanupStagedSYImports removes old ones). If the file no longer exists the function converts the os.Stat not-exist error into 'import task not found or expired', telling the caller to re-upload.","triggerScenarios":"Claiming a token whose staged zip was already claimed (renamed away), deleted by cleanupStagedSYImports due to age, or removed by a kernel restart clearing the temp staging dir.","commonSituations":"User takes too long between upload and claim; double-submit of the claim request; kernel restarted between staging and claiming; temp dir cleaned by the OS.","solutions":["Re-stage/re-upload the .sy zip and use the new token immediately.","Check the staging directory (temp/import) for the file before claiming.","Implement claim idempotency on the client: treat 'not found or expired' as needing a fresh upload, not a retry with the same token.","Increase/verify the staging retention window if imports legitimately take long before claiming."],"exampleFix":"// before\nawait claim(oldToken) // expired after restart\n\n// after\ntry { await claim(token) }\ncatch (e) {\n  if (String(e.msg).includes(\"not found or expired\")) {\n    token = await stage(uploadFile); await claim(token)\n  }\n}","handlingStrategy":"retry","validationCode":"const staged = await stat(join(tempDir, \"import\", token + \".zip\")); if (!staged) await restage()","typeGuard":null,"tryCatchPattern":"try { await claim(token) }\ncatch (e) { if (e.message === \"import task not found or expired\") { const t = await stage(file); await claim(t) } else throw e }","preventionTips":["Claim promptly after staging; do not leave staged imports idle","Treat claim as one-shot; never reuse a token after a successful claim","Re-stage after kernel restarts since temp staging is volatile"],"tags":["import","expiration","filesystem"],"backgroundTag":"file-not-found","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}