{"record":{"id":"c66b8f11efbc641a","repo":"siyuan-note/siyuan","slug":"2-conf-language-298-localized-already-used-free-trial","errorCode":"-2","errorMessage":"Conf.Language(298) (localized: already used free trial)","messagePattern":"Conf\\.Language\\(298\\) \\(localized: already used free trial\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/cloud_service.go","lineNumber":136,"sourceCode":"\trequest := httpclient.NewCloudRequest30s()\n\tresp, err := request.\n\t\tSetSuccessResult(requestResult).\n\t\tSetCookies(&http.Cookie{Name: \"symphony\", Value: user.UserToken}).\n\t\tPost(util.GetCloudServer() + \"/apis/siyuan/user/startFreeTrial\")\n\tif err != nil {\n\t\tlogging.LogErrorf(\"start free trial failed: %s\", err)\n\t\treturn ErrFailedToConnectCloudServer\n\t}\n\tif http.StatusUnauthorized == resp.StatusCode {\n\t\tinvalidUser()\n\t\treturn errors.New(Conf.Language(31))\n\t}\n\tif http.StatusOK != resp.StatusCode {\n\t\tlogging.LogErrorf(\"start free trial failed: %d\", resp.StatusCode)\n\t\treturn ErrFailedToConnectCloudServer\n\t}\n\tif -2 == requestResult.Code { // 已经试用订阅过\n\t\treturn errors.New(Conf.Language(298))\n\t}\n\tif 0 != requestResult.Code {\n\t\treturn errors.New(requestResult.Msg)\n\t}\n\treturn\n}\n\nfunc DeactivateUser() (err error) {\n\trelease := lockAssetSourceChange()\n\tdefer release()\n\tif Conf.Sync.Provider == conf.ProviderSiYuan {\n\t\tif err = requireCompleteAssetDownloads(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tuser := Conf.GetUser()\n\tif user == nil {\n\t\treturn errors.New(Conf.Language(31))","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/cloud_service.go#L118-L154","documentation":"StartFreeTrial calls the SiYuan cloud server to start a free subscription trial. When the cloud server responds with result code -2, it means this account has already consumed its free trial, so the kernel returns the localized message from Conf.Language(298) (\"already used free trial\"). This is an expected business rejection from the server, not a network or protocol failure.","triggerScenarios":"Calling StartFreeTrial (exposed via the subscription/trial API endpoint) with an account that has previously activated the free trial. The HTTP status is 200 but the JSON body carries Code == -2.","commonSituations":"A user re-installing SiYuan or switching devices tries the free trial again with the same SiYuan account; testing the trial endpoint repeatedly in development; a shared/team account where another member already used the trial.","solutions":["Do not retry: the trial is permanently consumed for this account; show the localized message to the user and stop","Purchase a subscription directly instead of using the trial (Settings - Account - Subscribe)","If the user believes this is wrong (never actually trialed), contact SiYuan support / check which account is logged in","Verify the intended account is logged in (Conf.GetUser()); a different account may still have an unused trial"],"exampleFix":"// before: blindly retrying on any error\nif err := model.StartFreeTrial(); err != nil {\n    util.PushErrMsg(err.Error(), 7000)\n    retryTrial() // loops forever for used trials\n}\n// after: treat trial-used as terminal, surface message once\nif err := model.StartFreeTrial(); err != nil {\n    util.PushErrMsg(err.Error(), 7000) // localized 'already used free trial'\n    // disable the trial button; guide user to purchase instead\n    disableTrialButton()\n}","handlingStrategy":"try-catch","validationCode":"// track trial usage client-side before offering the button\nif localStorage.getItem(\"siyuan-free-trial-used\") === \"true\" { hideTrialButton(); }","typeGuard":null,"tryCatchPattern":"try {\n  await startFreeTrial();\n} catch (e) {\n  if (e.message.includes(trialUsedMsg)) { disableTrialButton(); showSubscribeOption(); }\n  else { showError(e.message); }\n}","preventionTips":["Never auto-retry StartFreeTrial on error","Persist a local 'trial used' flag after first occurrence","Guide users to the purchase flow instead of the trial when this error appears"],"tags":["cloud","subscription","free-trial","business-rule","siyuan"],"backgroundTag":"api-error-response","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"}