{"record":{"id":"d044d7d0c3dd7e57","repo":"IceWhaleTech/CasaOS","slug":"e-error","errorCode":null,"errorMessage":"e.Error","messagePattern":"e\\.Error","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/dropbox/util.go","lineNumber":34,"sourceCode":")\n\nfunc (d *Dropbox) getRefreshToken() error {\n\turl := \"https://api.dropbox.com/oauth2/token\"\n\tvar resp base.TokenResp\n\tvar e TokenError\n\n\tres, err := base.RestyClient.R().SetResult(&resp).SetError(&e).\n\t\tSetFormData(map[string]string{\n\t\t\t\"code\":         d.Code,\n\t\t\t\"grant_type\":   \"authorization_code\",\n\t\t\t\"redirect_uri\": \"https://cloudoauth.files.casaos.app\",\n\t\t}).SetBasicAuth(d.Addition.AppKey, d.Addition.AppSecret).SetHeader(\"Content-Type\", \"application/x-www-form-urlencoded\").Post(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlogger.Info(\"get refresh token\", zap.String(\"res\", res.String()))\n\tif e.Error != \"\" {\n\t\treturn fmt.Errorf(e.Error)\n\t}\n\td.RefreshToken = resp.RefreshToken\n\treturn nil\n\n}\nfunc (d *Dropbox) refreshToken() error {\n\turl := \"https://api.dropbox.com/oauth2/token\"\n\tvar resp base.TokenResp\n\tvar e TokenError\n\n\tres, err := base.RestyClient.R().SetResult(&resp).SetError(&e).\n\t\tSetFormData(map[string]string{\n\t\t\t\"refresh_token\": d.RefreshToken,\n\t\t\t\"grant_type\":    \"refresh_token\",\n\t\t}).SetBasicAuth(d.Addition.AppKey, d.Addition.AppSecret).SetHeader(\"Content-Type\", \"application/x-www-form-urlencoded\").Post(url)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/IceWhaleTech/CasaOS/blob/0d3b2f444ec0193193cf03eef6d43c6e35b0183e/drivers/dropbox/util.go#L16-L52","documentation":"During the Dropbox OAuth authorization-code exchange, the token endpoint responded with an error payload (TokenError.Error non-empty). The driver wraps that raw error string with fmt.Errorf(e.Error) — a non-constant format string — and returns it. Typical underlying values are 'invalid_grant' or 'invalid_client'.","triggerScenarios":"Exchanging d.Code for a refresh token when the code is expired/already used, when the AppKey/AppSecret basic-auth credentials are wrong, or when the redirect_uri does not match the one used at authorization.","commonSituations":"User pasted an authorization code after it expired (~10 min) or re-did the flow; Dropbox app key/secret rotated; the 'cloudoauth.files.casaos.app' redirect was altered; clock skew or copying the code with whitespace.","solutions":["Generate a new authorization code and complete the exchange immediately (codes are single-use and short-lived).","Verify the Dropbox app's AppKey/AppSecret stored in d.Addition match the current app in the Dropbox App Console.","Confirm redirect_uri is exactly 'https://cloudoauth.files.casaos.app' both here and in the app console.","Check the logged response body (logger.Info \"get refresh token\") for the exact error_description."],"exampleFix":"// before (non-constant format string — also a govet printf issue)\nreturn fmt.Errorf(e.Error)\n\n// after\nreturn errors.New(e.Error)","handlingStrategy":"validation","validationCode":"if d.Addition.AppKey == \"\" || d.Addition.AppSecret == \"\" {\n\treturn errors.New(\"Dropbox AppKey/AppSecret must be configured before OAuth\")\n}","typeGuard":null,"tryCatchPattern":"if err := d.getRefreshToken(); err != nil {\n\tif strings.Contains(err.Error(), \"invalid_grant\") {\n\t\t// code expired/used: discard it and re-run the consent flow; never retry same code\n\t}\n\treturn err\n}","preventionTips":["Treat authorization codes as single-use and time-limited: fetch and exchange in one step","Keep AppKey/AppSecret in config validated at driver init","Register the exact redirect URI in the Dropbox console before deploying"],"tags":["dropbox","oauth","authorization-code","authentication"],"backgroundTag":null,"analyzedSha":"0d3b2f444ec0193193cf03eef6d43c6e35b0183e","analyzedAt":"2026-08-15T13:27:57.821Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}