{"record":{"id":"191da8e993f61203","repo":"AlistGo/alist","slug":"e-error-191da8","errorCode":null,"errorMessage":"e.Error","messagePattern":"e\\.Error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/google_photo/util.go","lineNumber":35,"sourceCode":"\tFETCH_SHARE_ALBUMS = \"share_albums\"\n)\n\nfunc (d *GooglePhoto) refreshToken() error {\n\turl := \"https://www.googleapis.com/oauth2/v4/token\"\n\tvar resp base.TokenResp\n\tvar e TokenError\n\t_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).\n\t\tSetFormData(map[string]string{\n\t\t\t\"client_id\":     d.ClientID,\n\t\t\t\"client_secret\": d.ClientSecret,\n\t\t\t\"refresh_token\": d.RefreshToken,\n\t\t\t\"grant_type\":    \"refresh_token\",\n\t\t}).Post(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif e.Error != \"\" {\n\t\treturn fmt.Errorf(e.Error)\n\t}\n\td.AccessToken = resp.AccessToken\n\treturn nil\n}\n\nfunc (d *GooglePhoto) request(url string, method string, callback base.ReqCallback, resp interface{}, headers map[string]string) ([]byte, error) {\n\treq := base.RestyClient.R()\n\treq.SetHeader(\"Authorization\", \"Bearer \"+d.AccessToken)\n\treq.SetHeader(\"Accept-Encoding\", \"gzip\")\n\tif headers != nil {\n\t\treq.SetHeaders(headers)\n\t}\n\n\tif callback != nil {\n\t\tcallback(req)\n\t}\n\tif resp != nil {\n\t\treq.SetResult(resp)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/google_photo/util.go#L17-L53","documentation":"Google Photo OAuth refresh failure: the token endpoint answered with an error string (typically invalid_grant or invalid_client) which the driver returns verbatim. Same shape as the Drive refresh error but on the Photos driver's refresh_token flow.","triggerScenarios":"refreshToken with a revoked/expired refresh token, wrong client secret, or consent-screen app in testing mode (refresh tokens expire in 7 days) — Google returns error in the token response and the driver propagates it.","commonSituations":"User revoked access via Google Account security page; client secret rotated; refresh token minted by a different OAuth client; system clock drift.","solutions":["Re-authenticate to obtain a new refresh token","Match client_id/client_secret to the project that issued the token","Publish the OAuth app or add test users if in testing status","Verify server time via NTP"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isPhotosTokenDead(err error) bool {\n  return err != nil && strings.Contains(err.Error(), \"invalid_grant\")\n}","tryCatchPattern":"if err := d.refreshToken(); err != nil {\n  if isPhotosTokenDead(err) { /* route user to re-consent; no automated retry will succeed */ }\n}","preventionTips":["Detect invalid_grant early and disable the storage instead of retry-looping","Keep OAuth client credentials and refresh token lifecycle-managed together"],"tags":["google-photos","oauth","refresh-token"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}