langgenius/dify · error · BadRequest
refresh_token is required
Error message
refresh_token is required
What it means
Flask BadRequest (HTTP 400) at oauth_server.py:229 in the REFRESH_TOKEN branch of the token endpoint. grant_type=refresh_token was accepted but payload.refresh_token is empty/None, so there is nothing to rotate. The service call OAuthServerService.sign_oauth_access_token is never reached.
Source
Thrown at api/controllers/console/auth/oauth_server.py:229
raise BadRequest("client_secret is invalid")
if payload.redirect_uri not in oauth_provider_app.redirect_uris:
raise BadRequest("redirect_uri is invalid")
access_token, refresh_token = OAuthServerService.sign_oauth_access_token(
grant_type, code=payload.code, client_id=oauth_provider_app.client_id
)
return jsonable_encoder(
{
"access_token": access_token,
"token_type": "Bearer",
"expires_in": OAUTH_ACCESS_TOKEN_EXPIRES_IN,
"refresh_token": refresh_token,
}
)
case OAuthGrantType.REFRESH_TOKEN:
if not payload.refresh_token:
raise BadRequest("refresh_token is required")
access_token, refresh_token = OAuthServerService.sign_oauth_access_token(
grant_type, refresh_token=payload.refresh_token, client_id=oauth_provider_app.client_id
)
return jsonable_encoder(
{
"access_token": access_token,
"token_type": "Bearer",
"expires_in": OAUTH_ACCESS_TOKEN_EXPIRES_IN,
"refresh_token": refresh_token,
}
)
@console_ns.route("/oauth/provider/account")
class OAuthServerUserAccountApi(Resource):
@setup_required
@console_ns.expect(console_ns.models[OAuthClientPayload.__name__])View on GitHub (pinned to ef8544b173)
Solutions
- Persist the refresh_token returned from the authorization_code exchange and pass it verbatim in the refresh request.
- If the refresh_token is genuinely lost, restart the flow at /oauth/provider/authorize to obtain a fresh code -> token pair.
- Validate the JSON body field name is exactly 'refresh_token' before sending.
Defensive patterns
Strategy: validation
Validate before calling
if (grantType === 'refresh_token' && !refreshToken) {
throw new Error('No refresh_token stored — re-authorize from scratch');
} Type guard
function hasRefreshToken(t: unknown): t is string { return typeof t === 'string' && t.length > 0; } Try / catch
try {
await token({grant_type:'refresh_token', refresh_token: refreshToken});
} catch (e) {
if (/refresh_token is required/i.test(e.message)) { restartOAuthFlow(); } else throw e;
} Prevention
- Persist refresh_token durably (not just in memory) after the code exchange.
- Send the field name exactly as 'refresh_token'.
- Re-authorize when the token is irrecoverably lost.
When it happens
Trigger: POST /oauth/provider/token with grant_type=refresh_token and a missing or empty refresh_token field. Usually a client that did not persist the refresh_token from the prior authorization_code exchange.
Common situations: Client lost the refresh_token (storage cleared, cookie expired, process restart without persistence) and attempts a refresh anyway; or the field name was mistyped in the JSON body.
Related errors
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/f31f3f312f280fbf.
Report an issue: GitHub.