{"record":{"id":"489fe9593f5e3211","repo":"siyuan-note/siyuan","slug":"account-authentication-failed-please-login-again","errorCode":null,"errorMessage":"Account authentication failed, please login again","messagePattern":"Account authentication failed, please login again","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/assets.go","lineNumber":1342,"sourceCode":"\t\t\tutil.PushUpdateMsg(msgId, msg, 3000)\n\t\t}\n\n\t\trequestResult := gulu.Ret.NewResult()\n\t\trequest := httpclient.NewCloudFileRequest2m()\n\t\tresp, reqErr := request.\n\t\t\tSetSuccessResult(requestResult).\n\t\t\tSetFile(\"file[]\", absAsset).\n\t\t\tSetCookies(&http.Cookie{Name: \"symphony\", Value: uploadToken}).\n\t\t\tSetHeader(\"meta-type\", metaType).\n\t\t\tSetHeader(\"biz-type\", bizType).\n\t\t\tPost(util.GetCloudServer() + \"/apis/siyuan/upload?ver=\" + util.Ver)\n\t\tif nil != reqErr {\n\t\t\tlogging.LogErrorf(\"upload assets failed: %s\", reqErr)\n\t\t\treturn count, ErrFailedToConnectCloudServer\n\t\t}\n\n\t\tif 401 == resp.StatusCode {\n\t\t\terr = errors.New(Conf.Language(31))\n\t\t\treturn\n\t\t}\n\n\t\tif 0 != requestResult.Code {\n\t\t\tlogging.LogErrorf(\"upload assets failed: %s\", requestResult.Msg)\n\t\t\terr = fmt.Errorf(Conf.Language(94), requestResult.Msg)\n\t\t\treturn\n\t\t}\n\n\t\tabsAsset = filepath.ToSlash(absAsset)\n\t\trelAsset := absAsset[strings.Index(absAsset, \"assets/\"):]\n\t\tcompletedUploadAssets = append(completedUploadAssets, relAsset)\n\t\tlogging.LogInfof(\"uploaded asset [%s]\", relAsset)\n\t\tcount++\n\t}\n\n\tif !ignorePushMsg {\n\t\tutil.PushClearMsg(msgId)","sourceCodeStart":1324,"sourceCodeEnd":1360,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/assets.go#L1324-L1360","documentation":"Returned by UploadAssets2Cloud (assets.go:1342) when the SiYuan cloud upload endpoint /apis/siyuan/upload responds with HTTP 401. The text is the kernel i18n key 31 (\"Account authentication failed, please login again\"). The request carries a symphony cookie whose value is the upload token fetched from LoadUploadToken; the 401 means that token (or the underlying user account session) is no longer accepted by the cloud server.","triggerScenarios":"Calling asset cloud upload (action path uploadAssets2Cloud with bizType \"upload-assets\") or export-to-Liandi (bizType \"export-liandi\") after the user's account session expired, after logout/relogin on another device, or when LoadUploadToken silently obtained a token for an account whose UserToken is stale. The guard fires only when resp.StatusCode == 401, distinct from a network error (ErrFailedToConnectCloudServer) and from a non-zero business code (error 421).","commonSituations":"User logged out of the SiYuan account in another client but kept working locally; token cache (uploadToken/uploadTokenTime, refreshed at most hourly) holds a token bound to a dead session; clock skew on the server invalidating the session; subscription ended and the account was de-authenticated.","solutions":["Re-login to the SiYuan cloud account (Settings - About - Account - login) so Conf.GetUser().UserToken is refreshed, then retry the upload; LoadUploadToken will fetch a fresh upload token.","If re-login does not clear it, restart the kernel so the in-memory uploadToken/uploadTokenTime are reset and the next call forces LoadUploadToken to request a new token.","Verify network reachability of util.GetCloudServer() and that no proxy strips the symphony cookie; a 401 here is auth, not connectivity (connectivity yields ErrFailedToConnectCloudServer)."],"exampleFix":"// before: upload attempted with a possibly stale cached token\ncount, err = uploadAssets2Cloud(assets, bizTypeUploadAssets, ignorePushMsg)\n\n// after: force token refresh when a 401-shaped error is observed, then retry once\nif errors.Is(err, ErrFailedToConnectCloudServer) || strings.Contains(fmt.Sprint(err), Conf.Language(31)) {\n    uploadTokenTime = 0 // invalidate cache so LoadUploadToken re-fetches\n    if loadErr := LoadUploadToken(); loadErr == nil {\n        count, err = uploadAssets2Cloud(assets, bizTypeUploadAssets, ignorePushMsg)\n    }\n}","handlingStrategy":"validation","validationCode":"// Before triggering cloud upload, ensure the account session is alive.\nif (!window.siyuan.user || !window.siyuan.user.userToken) {\n  showMessage(window.siyuan.languages[31]) // prompt re-login\n  return\n}\nawait uploadAssets2Cloud(assets, \"upload-assets\")","typeGuard":null,"tryCatchPattern":"// Distinguish auth (401) from transport and business errors.\ntry {\n  await fetchPost('/api/asset/uploadCloud', { ... })\n} catch (e) {\n  const msg = String(e.message || e)\n  if (msg.includes(window.siyuan.languages[31])) {\n    // auth stale -> re-login flow, then retry once\n    await openAccountLogin()\n  } else if (msg.includes('failed to connect cloud server')) {\n    // transport; do not retry blindly\n  } else {\n    // business error (421); surface server message\n    showMessage(msg)\n  }\n}","preventionTips":["Refresh the account session before any cloud upload if the user token is older than the session lifetime.","Treat a 401 as a signal to invalidate the cached upload token (set uploadTokenTime = 0) so the next call re-fetches.","Do not auto-retry a 401 in a tight loop; re-authenticate first."],"tags":["cloud","upload","auth","assets","i18n","session"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}