{"record":{"id":"90ed929f12300c97","repo":"windmill-labs/windmill","slug":"sql-job-on-http-connection","errorCode":null,"errorMessage":"sql job on http connection","messagePattern":"sql job on http connection","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/worker.rs","lineNumber":1990,"sourceCode":"        windmill_common::log_context::spawn_with_log_context(async move {\n            async move {\n                match insert_wait_time(job_id, root_job_id, &db, wait_time).await {\n                    Ok(()) => tracing::warn!(\"job {job_id} waited for an executor for a significant amount of time. Recording value wait_time={}ms\", wait_time),\n                    Err(e) => tracing::error!(\"Failed to insert outstanding wait time: {}\", e),\n                }\n            }\n            .instrument(span)\n            .await\n        });\n    }\n}\n\nasync fn extract_job_and_perms(job: NextJob, conn: &Connection) -> JobAndPerms {\n    match (job, conn) {\n        (NextJob::Sql { job, flow_runners, .. }, Connection::Sql(db)) => {\n            JobAndPerms { flow_runners, ..job.get_job_and_perms(db).await }\n        }\n        (NextJob::Sql { .. }, Connection::Http(_)) => panic!(\"sql job on http connection\"),\n        (NextJob::Http(job), _) => job,\n    }\n}\n\npub fn create_span_with_name(\n    arc_job: &MiniPulledJob,\n    worker_name: &str,\n    hostname: Option<&str>,\n    span_name: &str,\n) -> Span {\n    // The span macro requires a literal, so we use a fixed name and set otel.name dynamically\n    let span = tracing::span!(\n        tracing::Level::INFO,\n        \"job\",\n        job_id = %arc_job.id,\n        root_job = field::Empty,\n        workspace_id = %arc_job.workspace_id,\n        worker = %worker_name,","sourceCodeStart":1972,"sourceCodeEnd":2008,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/worker.rs#L1972-L2008","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Report this to Windmill maintainers with worker logs — it indicates a job-routing bug, not a misconfiguration","Ensure workers that can execute SQL jobs pull through the SQL connection path (not HTTP-only)","Check your worker capability configuration (which queues/kinds the worker pulls) matches the deployed job kinds","If running a modified build, fix the dispatch so NextJob::Sql is only matched with Connection::Sql"],"exampleFix":"// before: pulling sql jobs over http connection\nlet job = pull_job(&Connection::Http(client), ...);\n// after: pull sql jobs with the sql connection\nlet job = pull_job(&Connection::Sql(db), ...);","handlingStrategy":"validation","validationCode":"// When integrating job pulls, assert kind/connection compatibility first\nfunction canPull(jobKind, conn) {\n  return !(jobKind === 'sql' && conn.type !== 'sql');\n}","typeGuard":"function isSqlJobOnHttp(job, conn) {\n  return job && job.kind === 'Sql' && conn && conn.type === 'http';\n}","tryCatchPattern":null,"preventionTips":["Keep job-pull connection type and worker capabilities in sync","Add unit tests covering every (NextJob variant, Connection variant) pair","Alert on this panic in production — it signals a dispatch bug, not user error","Avoid HTTP-only pull paths for SQL-capable workers when forking/extending the puller"],"tags":["rust","worker","sql","invariant","routing"],"backgroundTag":"job-routing-mismatch","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}