databendlabs/databend · error

login_handler expect session id in ctx

Error message

login_handler expect session id in ctx

What it means

The HTTP login handler requires the client to present a session id (`client_session_id`) in the request context. `expect("login_handler expect session id in ctx")` panics when the extractor that populates `ctx.client_session_id` did not run or the request omitted the session-id cookie/header, instead of returning a clean 400.

Solutions

  1. Send the required session id (cookie or header) with the login request.
  2. Verify middleware/extractors that set client_session_id are attached to the login route.
  3. Replace the expect with a proper poem error: return 400 bad_request when client_session_id is missing.
  4. Check reverse-proxy config isn't dropping cookies/custom headers.

Example fix

// before
let session_id = ctx.client_session_id.as_ref().expect("login_handler expect session id in ctx").clone();
// after
let session_id = ctx.client_session_id.clone().ok_or_else(||
    poem::Error::from_string("session id is required", poem::http::StatusCode::BAD_REQUEST))?;
Defensive patterns

Strategy: validation

Validate before calling

// client: ensure session id is attached
if (!headers.has('x-databend-session-id') && !cookies.has('session_id')) {
  throw new Error('login requires a session id cookie/header');
}

Type guard

fn has_session_id(ctx: &HttpContext) -> bool { ctx.client_session_id.is_some() }

Try / catch

// server-side fix
match ctx.client_session_id.as_ref() {
    Some(id) => id.clone(),
    None => return Err(poem::Error::from_string("missing session id", poem::http::StatusCode::BAD_REQUEST)),
}

Prevention

When it happens

Trigger: POST /v1/session/login without the session-id cookie or header that the session middleware/extractor normally injects; calling the handler outside the configured router (e.g. tests or proxied requests stripping headers).

Common situations: See trigger scenarios.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/5dc93c3c0e8e139b. Report an issue: GitHub.

Appendix: source

Thrown at src/query/service/src/servers/http/v1/session/login_handler.rs:103

#[derive(Deserialize)]
struct LoginQuery {
    disable_session_token: Option<bool>,
}

///  # For client/driver developer:
/// - It is encouraged to call `/v1/session/login` when establishing connection, not mandatory for now.
/// - May get 404 when talk to old server, may check `/health` (no `/v1` prefix) to ensure the host:port is not wrong.
#[poem::handler]
#[async_backtrace::framed]
pub async fn login_handler(
    ctx: &HttpQueryContext,
    Json(req): Json<LoginRequest>,
    Query(query): Query<LoginQuery>,
) -> PoemResult<impl IntoResponse> {
    let session_id = ctx
        .client_session_id
        .as_ref()
        .expect("login_handler expect session id in ctx")
        .clone();
    check_login(ctx, &req)
        .await
        .map_err(HttpErrorCode::bad_request)?;
    let version = &ctx.version.semantic;
    let server_arrow_features = (SERVER_MAX_ARROW_RESULT_VERSION
        >= ARROW_FEATURE_NEGOTIATION_VERSION)
        .then_some(ArrowFeatures::decimal64_enabled());
    let id_only = || {
        Ok(Json(LoginResponse {
            version: version.to_string(),
            session_id: session_id.clone(),
            server_max_arrow_result_version: SERVER_MAX_ARROW_RESULT_VERSION,
            server_arrow_features: server_arrow_features.clone(),
            tokens: None,
        }))
    };

View on GitHub (pinned to 288d84d76e)