{"record":{"id":"08552e9322cd05a9","repo":"siyuan-note/siyuan","slug":"request-user-failed","errorCode":null,"errorMessage":"request user failed","messagePattern":"request user failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/cloud_service.go","lineNumber":700,"sourceCode":"\t\tdesc = videoRegexp.ReplaceAllString(desc, \" 视频 \")\n\t\tdesc = fileRegexp.ReplaceAllString(desc, \" 文件 \")\n\t\tdesc = strings.ReplaceAll(desc, \"\\n\\n\", \"\")\n\t\tdesc = strings.TrimSpace(desc)\n\t\tshorthand[\"shorthandDesc\"] = desc\n\n\t\tmd := shorthand[\"shorthandContent\"].(string)\n\t\tshorthand[\"shorthandMd\"] = md\n\t\ttree := parse.Parse(\"\", []byte(md), luteEngine.ParseOptions)\n\t\tluteEngine.RenderOptions.ProtyleMarkNetImg = false\n\t\tcontent := luteEngine.ProtylePreview(tree, luteEngine.RenderOptions, luteEngine.ParseOptions)\n\t\tshorthand[\"shorthandContent\"] = content\n\t}\n\treturn\n}\n\nvar (\n\terrInvalidUser       = errors.New(\"invalid user\")\n\terrRequestUserFailed = errors.New(\"request user failed\")\n)\n\nfunc getUser(token string) (*conf.User, error) {\n\tresult := map[string]any{}\n\trequest := httpclient.NewCloudRequest30s().SetRetryCount(0)\n\tresp, err := request.\n\t\tSetSuccessResult(&result).\n\t\tSetBody(map[string]string{\"token\": token}).\n\t\tPost(util.GetCloudServer() + \"/apis/siyuan/user\")\n\tif err != nil {\n\t\tlogging.LogErrorf(\"get community user failed: %s\", err)\n\t\treturn nil, errRequestUserFailed\n\t}\n\tif http.StatusOK != resp.StatusCode {\n\t\tlogging.LogErrorf(\"get community user failed: %d\", resp.StatusCode)\n\t\tif http.StatusUnauthorized == resp.StatusCode {\n\t\t\treturn nil, errInvalidUser\n\t\t}","sourceCodeStart":682,"sourceCodeEnd":718,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/cloud_service.go#L682-L718","documentation":"errRequestUserFailed (\"request user failed\") is returned by getUser and RefreshUser when the cloud /apis/siyuan/user request fails for reasons other than an invalid token: transport errors (network unreachable, DNS, timeout — retries disabled via SetRetryCount(0)), non-200/non-401 HTTP statuses, a UserTokenExpireTime string that fails integer parsing, or a concurrent login race detected in RefreshUser (Conf.GetUser() changed mid-refresh).","triggerScenarios":"No network / cloud server unreachable during user refresh; cloud returning 5xx; malformed user.UserTokenExpireTime failing strconv.ParseInt (cloud_service.go:421); another goroutine changing Conf.GetUser() between snapshot and commit (cloud_service.go:441).","commonSituations":"Offline or behind a proxy/firewall blocking siyuan cloud; intermittent DNS failures; corporate networks intercepting TLS; clock/format corruption of the expire-time field in conf.json; concurrent UI actions triggering parallel refreshes.","solutions":["Check network connectivity to the configured cloud server (util.GetCloudServer()) and any proxy/firewall settings, then retry — unlike errInvalidUser this error is transient and safe to retry.","Inspect kernel logs (\"get community user failed\" / \"convert token expire time ... failed\") to identify whether the cause was transport, HTTP status, or expire-time parsing.","If caused by expire-time parsing, log out and back in to rewrite a clean UserData in conf.json.","If it came from the concurrent-refresh race, simply retry; the latest Conf.GetUser() is returned with this sentinel error and state remains consistent.","Distinguish from auth failure with model.IsInvalidUserRefresh(err) — only that case requires re-login."],"exampleFix":"// before\nuser, err := model.RefreshUser()\nif err != nil { return } // treats transient outage as fatal\n// after\nuser, err := model.RefreshUser()\nif err != nil {\n    if model.IsInvalidUserRefresh(err) {\n        model.LogoutUser(); return\n    }\n    // transient: retry with backoff\n    time.Sleep(retryDelay)\n    user, err = model.RefreshUser()\n}","handlingStrategy":"retry","validationCode":"// check reachability of the cloud server before refreshing the user\nconn, err := net.DialTimeout(\"tcp\", hostPort(util.GetCloudServer()), 5*time.Second)\nif err != nil { /* offline: defer refresh */ } else { conn.Close() }","typeGuard":"func isTransientUserErr(err error) bool { return errors.Is(err, model.ErrRequestUserFailedEquivalent) || !model.IsInvalidUserRefresh(err) }","tryCatchPattern":"user, err := model.RefreshUser()\nif err != nil && !model.IsInvalidUserRefresh(err) {\n    // transient: retry with exponential backoff\n    for i := 0; i < 3; i++ { time.Sleep(backoff(i)); if user, err = model.RefreshUser(); err == nil { break } }\n}","preventionTips":["Treat this error as retryable; only errInvalidUser requires logout","Check network/proxy/firewall when it recurs","Keep conf.json UserTokenExpireTime well-formed; re-login if corrupted","Serialize user refresh calls to avoid the concurrent-login race guard"],"tags":["cloud","network","siyuan"],"backgroundTag":"network-request-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}