windmill-labs/windmill · error

sql job on http connection

Error message

sql job on http connection

What it means

Jobs are pulled from the database either through the internal SQL connection or via the HTTP API. `extract_job_and_perms` extracts job permissions using the connection that matches the job kind: SQL jobs require a Connection::Sql (direct database handle) to read their perms. If a SQL job arrives over an HTTP connection there is no DB handle to authorize it, so the process panics — this is an internal invariant, meaning a routing bug rather than user error.

Source

Thrown at backend/windmill-worker/src/worker.rs:1990

        windmill_common::log_context::spawn_with_log_context(async move {
            async move {
                match insert_wait_time(job_id, root_job_id, &db, wait_time).await {
                    Ok(()) => tracing::warn!("job {job_id} waited for an executor for a significant amount of time. Recording value wait_time={}ms", wait_time),
                    Err(e) => tracing::error!("Failed to insert outstanding wait time: {}", e),
                }
            }
            .instrument(span)
            .await
        });
    }
}

async fn extract_job_and_perms(job: NextJob, conn: &Connection) -> JobAndPerms {
    match (job, conn) {
        (NextJob::Sql { job, flow_runners, .. }, Connection::Sql(db)) => {
            JobAndPerms { flow_runners, ..job.get_job_and_perms(db).await }
        }
        (NextJob::Sql { .. }, Connection::Http(_)) => panic!("sql job on http connection"),
        (NextJob::Http(job), _) => job,
    }
}

pub fn create_span_with_name(
    arc_job: &MiniPulledJob,
    worker_name: &str,
    hostname: Option<&str>,
    span_name: &str,
) -> Span {
    // The span macro requires a literal, so we use a fixed name and set otel.name dynamically
    let span = tracing::span!(
        tracing::Level::INFO,
        "job",
        job_id = %arc_job.id,
        root_job = field::Empty,
        workspace_id = %arc_job.workspace_id,
        worker = %worker_name,

View on GitHub (pinned to e474e8803c)

Solutions

  1. Report this to Windmill maintainers with worker logs — it indicates a job-routing bug, not a misconfiguration
  2. Ensure workers that can execute SQL jobs pull through the SQL connection path (not HTTP-only)
  3. Check your worker capability configuration (which queues/kinds the worker pulls) matches the deployed job kinds
  4. If running a modified build, fix the dispatch so NextJob::Sql is only matched with Connection::Sql

Example fix

// before: pulling sql jobs over http connection
let job = pull_job(&Connection::Http(client), ...);
// after: pull sql jobs with the sql connection
let job = pull_job(&Connection::Sql(db), ...);
Defensive patterns

Strategy: validation

Validate before calling

// When integrating job pulls, assert kind/connection compatibility first
function canPull(jobKind, conn) {
  return !(jobKind === 'sql' && conn.type !== 'sql');
}

Type guard

function isSqlJobOnHttp(job, conn) {
  return job && job.kind === 'Sql' && conn && conn.type === 'http';
}

Prevention

When it happens

Trigger: A NextJob::Sql job dispatched to a worker/runner whose connection is Connection::Http — i.e. the job puller fetched a SQL job on the HTTP path, typically after a code change in job dispatch or mismatched worker capabilities.

Common situations: Custom forks/patches to the pull loop mixing job kinds and connection types; running a worker configured for HTTP job pull that receives SQL jobs due to a dispatch/queue misconfiguration; developing a new job kind and wiring it through the wrong connection variant.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/90ed929f12300c97. Report an issue: GitHub.