langgenius/dify · error · BadRequest

client_id is required

Error message

client_id is required

What it means

Flask BadRequest (HTTP 400) raised by the oauth_server_client_id_required decorator at oauth_server.py:84 when request.get_json() returns None — i.e. the request body is empty, not JSON, or has the wrong Content-Type. It guards every OAuth-server endpoint that needs a client_id (POST /oauth/provider, /authorize, /token, /account). The message is 'client_id is required' even though the real cause is a missing/invalid JSON body.

Source

Thrown at api/controllers/console/auth/oauth_server.py:84

register_schema_models(console_ns, OAuthClientPayload, OAuthProviderRequest, OAuthTokenRequest)
register_response_schema_models(
    console_ns,
    OAuthProviderAccountResponse,
    OAuthProviderAppResponse,
    OAuthProviderAuthorizeResponse,
    OAuthProviderTokenResponse,
)


def oauth_server_client_id_required[T, **P, R](
    view: Callable[Concatenate[T, OAuthProviderApp, P], R],
) -> Callable[Concatenate[T, P], R]:
    @wraps(view)
    def decorated(self: T, *args: P.args, **kwargs: P.kwargs) -> R:
        json_data = request.get_json()
        if json_data is None:
            raise BadRequest("client_id is required")

        payload = OAuthClientPayload.model_validate(json_data)
        client_id = payload.client_id

        oauth_provider_app = OAuthServerService.get_oauth_provider_app(client_id)
        if not oauth_provider_app:
            raise NotFound("client_id is invalid")

        return view(self, oauth_provider_app, *args, **kwargs)

    return decorated


def oauth_server_access_token_required[T, **P, R](
    view: Callable[Concatenate[T, OAuthProviderApp, Account, P], R],
) -> Callable[Concatenate[T, OAuthProviderApp, P], R | ResponseReturnValue]:
    @wraps(view)
    def decorated(

View on GitHub (pinned to ef8544b173)

Solutions

  1. Send the request body as JSON with header Content-Type: application/json and include {"client_id": "..."}.
  2. If integrating a standards-compliant OAuth client that posts form data, add an adapter to translate form fields to JSON before calling this endpoint.
  3. Confirm the body is not empty — even valid JSON with a missing client_id would fail later at OAuthClientPayload.model_validate, not here.
  4. Check for a proxy/middleware stripping the request body or Content-Type.

Example fix

# before
curl -X POST $URL/oauth/provider -d 'client_id=abc'
# after
curl -X POST $URL/oauth/provider -H 'Content-Type: application/json' -d '{"client_id":"abc"}'
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a JSON body with client_id before calling any /oauth/provider* endpoint.
function buildBody(clientId, extra = {}) {
  if (!clientId) throw new Error('client_id required');
  return JSON.stringify({client_id: clientId, ...extra});
}
await fetch('/console/api/oauth/provider', {
  method: 'POST', headers: {'Content-Type': 'application/json'}, body: buildBody(cid)
});

Type guard

function isJsonObject(v): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null;
}

Try / catch

try {
  await callOAuthServer(cid);
} catch (e) {
  if (/client_id is required/i.test(e.message)) {
    throw new Error('Send a JSON body with Content-Type: application/json');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any of the /console/api/oauth/provider* endpoints with an empty body, form-encoded body, or Content-Type other than application/json. request.get_json() returns None and the decorator raises before OAuthClientPayload is even parsed.

Common situations: OAuth client integration sends URL-encoded form data (common for standard OAuth token endpoints) instead of JSON; or a curl/test client omits the body entirely. Developers expect RFC 6749 form-post behavior but this server expects JSON.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/f1309baec2539c2f. Report an issue: GitHub.