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

  1. Persist the refresh_token returned from the authorization_code exchange and pass it verbatim in the refresh request.
  2. If the refresh_token is genuinely lost, restart the flow at /oauth/provider/authorize to obtain a fresh code -> token pair.
  3. 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

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.