{"record":{"id":"168faf807efdbc7b","repo":"Dokploy/dokploy","slug":"failed-to-refresh-token-response-statustext","errorCode":null,"errorMessage":"Failed to refresh token: ${response.statusText}","messagePattern":"Failed to refresh token: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/utils/providers/gitlab.ts","lineNumber":42,"sourceCode":"\t}\n\n\t// Use internal URL for token refresh when GitLab is on same instance as Dokploy\n\tconst baseUrl = gitlabProvider.gitlabInternalUrl || gitlabProvider.gitlabUrl;\n\tconst response = await fetch(`${baseUrl}/oauth/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\trefresh_token: gitlabProvider.refreshToken as string,\n\t\t\tclient_id: gitlabProvider.applicationId as string,\n\t\t\tclient_secret: gitlabProvider.secret as string,\n\t\t}),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to refresh token: ${response.statusText}`);\n\t}\n\n\tconst data = await response.json();\n\n\tconst expiresAt = data.expires_in\n\t\t? Math.floor(Date.now() / 1000) + data.expires_in\n\t\t: null;\n\n\tawait updateGitlab(gitlabProviderId, {\n\t\taccessToken: data.access_token,\n\t\trefreshToken: data.refresh_token,\n\t\texpiresAt,\n\t});\n\treturn data;\n};\n\nexport const haveGitlabRequirements = (gitlabProvider: Gitlab) => {\n\treturn !!(gitlabProvider?.accessToken && gitlabProvider?.refreshToken);","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/Dokploy/dokploy/blob/546686ea3587f12ec5652217dedd9f7960fb6d15/packages/server/src/utils/providers/gitlab.ts#L24-L60","documentation":"Thrown when GitLab's OAuth token refresh endpoint returns a non-2xx response. refreshGitlabToken POSTs grant_type=refresh_token with the provider's applicationId/secret; any HTTP failure (400 invalid_grant, 401 bad client credentials, network proxy error) surfaces as this generic statusText message.","triggerScenarios":"Refresh token expired or revoked (user revoked app access in GitLab), wrong applicationId/secret for the OAuth app, GitLab instance unreachable behind a proxy, or redirect/app configuration mismatch on a self-hosted GitLab.","commonSituations":"Long-lived Dokploy install where the stored GitLab refresh token expired; rotating the GitLab OAuth app secret without updating the provider; self-hosted GitLab with certificate or network issues.","solutions":["Re-authenticate the GitLab provider (re-do the OAuth flow) to get a fresh refresh token","Verify the GitLab OAuth application ID and secret stored on the provider match the GitLab app","Log response.status and the JSON body (error/error_description) instead of statusText to see the real cause","Check network/DNS/TLS reachability of the GitLab instance from the server"],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(`Failed to refresh token: ${response.statusText}`);\n}\n\n// after\nif (!response.ok) {\n  const body = await response.text();\n  throw new Error(`Failed to refresh token: ${response.status} ${response.statusText} ${body}`);\n}","handlingStrategy":"retry","validationCode":"const provider = await findGitlabById(gitlabId);\nif (!provider?.refreshToken) throw new Error('Re-authenticate the GitLab provider');","typeGuard":"const canRefresh = (p: unknown): p is { refreshToken: string; applicationId: string; secret: string } =>\n  typeof p === 'object' && p !== null &&\n  typeof (p as any).refreshToken === 'string' && (p as any).refreshToken.length > 0;","tryCatchPattern":"try {\n  await refreshGitlabToken(gitlabId);\n} catch (e) {\n  // token revoked — force re-auth flow, don't retry in a loop\n  throw new Error(`Re-authentication required: ${e instanceof Error ? e.message : e}`);\n}","preventionTips":["Re-authenticate the provider when refresh fails rather than retrying blindly","Keep the OAuth app secret in sync with GitLab","Log response bodies on refresh failure for diagnosability"],"tags":["gitlab","oauth","refresh-token","http"],"backgroundTag":"oauth-refresh-token-failed","analyzedSha":"546686ea3587f12ec5652217dedd9f7960fb6d15","analyzedAt":"2026-08-27T05:18:58.095Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}