{"record":{"id":"ea28eef557eda7fa","repo":"AlistGo/alist","slug":"failed-to-refresh-token-s-ea28ee","errorCode":null,"errorMessage":"failed to refresh token: %s","messagePattern":"failed to refresh token: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"drivers/dropbox/util.go","lineNumber":38,"sourceCode":"\t\turl = d.OauthTokenURL\n\t}\n\tvar tokenResp TokenResp\n\tresp, err := base.RestyClient.R().\n\t\t//ForceContentType(\"application/x-www-form-urlencoded\").\n\t\t//SetBasicAuth(d.ClientID, d.ClientSecret).\n\t\tSetFormData(map[string]string{\n\t\t\t\"grant_type\":    \"refresh_token\",\n\t\t\t\"refresh_token\": d.RefreshToken,\n\t\t\t\"client_id\":     d.ClientID,\n\t\t\t\"client_secret\": d.ClientSecret,\n\t\t}).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Debugf(\"[dropbox] refresh token response: %s\", resp.String())\n\tif resp.StatusCode() != 200 {\n\t\treturn fmt.Errorf(\"failed to refresh token: %s\", resp.String())\n\t}\n\t_ = utils.Json.UnmarshalFromString(resp.String(), &tokenResp)\n\td.AccessToken = tokenResp.AccessToken\n\top.MustSaveDriverStorage(d)\n\treturn nil\n}\n\nfunc (d *Dropbox) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {\n\treq := base.RestyClient.R()\n\treq.SetHeader(\"Authorization\", \"Bearer \"+d.AccessToken)\n\tif d.RootNamespaceId != \"\" {\n\t\tapiPathRootJson, err := utils.Json.MarshalToString(map[string]interface{}{\n\t\t\t\".tag\": \"root\",\n\t\t\t\"root\": d.RootNamespaceId,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/dropbox/util.go#L20-L56","documentation":"Thrown when the Dropbox OAuth token endpoint (POST with grant_type=refresh_token) answers with a non-200 status. The response body is included in the message and typically contains error and error_description fields such as invalid_grant or invalid_client. This is a hard authentication configuration failure, not a transient error.","triggerScenarios":"Expired, revoked, or single-use refresh token (invalid_grant); mismatched client_id/client_secret (invalid_client); the refresh token was issued for a different Dropbox app than the one configured; Dropbox app is disabled.","commonSituations":"Reusing a refresh token after regenerating the app secret; copying the app key into the secret field; rotating tokens in another tool which invalidated the old one; token issued in production mode used against a development-mode app for an uninvited user.","solutions":["Read the response body in the message: invalid_grant means generate a NEW refresh token via the OAuth flow; invalid_client means fix ClientID/ClientSecret","Confirm ClientID and ClientSecret come from the same Dropbox app page that produced the refresh token","If the app was recreated, repeat the full authorization flow and update all three fields","After fixing, restart/reload the driver so Init re-runs cleanly"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// refreshToken\nif err := d.refreshToken(); err != nil {\n    if strings.Contains(err.Error(), \"failed to refresh token\") {\n        // fatal config error: stop retrying and flag storage as needing re-auth\n        log.Errorf(\"dropbox refresh token rejected: %s\", err)\n        return err\n    }\n    return err // transient network error: safe to retry later\n}","preventionTips":["Treat invalid_grant/invalid_client as permanent — surface to the user instead of retry loops","Keep client_id, client_secret, and refresh_token from the same Dropbox app in one config unit","Rotate all three values together whenever the app secret changes"],"tags":["dropbox","oauth","refresh-token","auth"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}