Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Diagnose broken or unexplained Databricks compute — slow cold starts, failed cluster launches, Photon paying its premium without the speedup, DBR-upgrade landmines, and spot-interruption shuffle aborts — by correlating a cluster's live event stream across API surfaces. Use when a Databricks cluster won't start, died mid-run, is randomly slow to start, when planning a Databricks Runtime upgrade, or when a job keeps failing on spot loss. Trigger with "databricks cluster won't start", "cluster fail
.claude/skills/jeremylongshore-databricks-cluster-forensics/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 187% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 94% | 0% |
The operational SRE spine of the pack — what a Databricks engineer reaches for at 2 AM when the compute layer is broken or unexplained. It correlates a cluster's live event stream across API surfaces to name the failure with its actual error code and its version-specific mitigation, not "network problem, try again".
Six real compute-layer failures live in this skill; each has a deterministic detector and an on-demand reference:
start randomly takes 20-35, and Databricks reports only the aggregate. scripts/cluster-coldstart-forensics.py splits the PENDING window into stages (provisioning / init-scripts / spark-startup) so you see which stage spiked.
Spark on UDFs while the cluster still bills the ~2× Photon DBU premium for its whole uptime. See references/photon-eligibility-and-fallback.md.
workspace filesystem (a ~500 MB cap that silently breaks large intermediate writes); 15.1 removed DBFS-root library storage and JDK 11; 15.4 flipped a JDBC calendar default. scripts/find-cwd-writes.py (AST) and scripts/scan-jar-jdk.sh (bytecode target) are the pre-upgrade detectors; references/dbr-upgrade-paths.md is the per-hop encyclopedia.
CLOUD_PROVIDER_LAUNCH_FAILURE /NPIP_TUNNEL_SETUP_FAILURE each hide five distinct causes (subnet IP exhaustion, DNS, NSG/security-group block, deleted VNet, cloud throttling). references/termination-codes.md disambiguates them.
recompute; lose another mid-recompute and the stage exceeds spark.stage.maxConsecutiveAttempts and the job aborts. references/spot-vs-ondemand-decision.md is the config decision tree.
It is architecturally distinct from the v1 databricks-common-errors and databricks-incident-runbook skills: those narrate. This one reads live cluster events, buckets them deterministically (the arithmetic is in scripts/, never eyeballed), fans out parallel root-cause threads via the cluster-event-investigator subagent, and loads deep knowledge from references/ only when a symptom needs it.
Two data planes. Cluster control-plane evidence (spec, state, event stream) comes from the custom databricks-workspace-mcp (clusters_get / clusters_events / clusters_list). The Photon audit's system.query.history read runs through the CLI Statement Execution API (databricks api post /api/2.0/sql/statements) — the same path databricks-cost-leak-hunter uses. Either surface absent, the skill degrades to advisory mode and accepts pasted event JSON / query plans so it still produces value.
databricks-workspace-mcp registered — the source of clusters_get,clusters_events, clusters_list. Absent, the skill accepts a pasted clusters.events response and says so (advisory mode).
databricks auth login, or theDATABRICKS_HOST + DATABRICKS_TOKEN env pair) and jq — for the Photon system.query.history read.
DATABRICKS_WAREHOUSE_ID set to a running SQL warehouse — required only forthe Photon audit (Step 2); the cold-start / launch-failure flows need only the workspace MCP.
unzip (and ideally a JDK's javap) on PATH for the DBR-15.1 JAR scan(scan-jar-jdk.sh falls back to reading class-file bytes if javap is absent).
The skill checks which surfaces are present in Step 0 and reports what is missing before starting a flow it cannot finish.
Pick the flow by symptom. Each is independent; run only what the question needs.
Confirm the workspace MCP answers (clusters_list returns) and, for a Photon audit, that the CLI is authenticated and DATABRICKS_WAREHOUSE_ID is set. Name any missing surface and switch that flow to advisory mode (pasted input) rather than failing mid-diagnosis.
Pull the cluster's event stream and bucket its PENDING time:
bash# events from the workspace MCP (clusters_events) or the CLI, saved to a file: databricks clusters events --cluster-id "$CLUSTER_ID" --output json > "$OUT/events.json" python3 "${CLAUDE_SKILL_DIR}/scripts/cluster-coldstart-forensics.py" \ --input "$OUT/events.json"
provisioning → cloud VM allocation or network/DNS/NPIP; init-scripts → a slow init script or library install; spark-startup → driver spin-up.
termination_reason.code anddisambiguate with ${CLAUDE_SKILL_DIR}/references/termination-codes.md — especially the CLOUD_PROVIDER_LAUNCH_FAILURE / NPIP_TUNNEL_SETUP_FAILURE umbrella and its five sub-causes.
For a messy failure, hand the cluster_id to the cluster-event-investigator subagent (/investigate-cluster <id>): it fans out one thread per cause class and returns the single most-likely cause with its evidence.
Check whether Photon is earning its premium. Query recent query history for plans that fell back to Spark, then corroborate the cluster is Photon (runtime_engine via clusters_get):
bashdatabricks api post /api/2.0/sql/statements --json "$(jq -n --arg wh "$DATABRICKS_WAREHOUSE_ID" \ '{warehouse_id:$wh, wait_timeout:"30s", statement:"SELECT statement_id, executed_by, total_duration_ms FROM system.query.history WHERE end_time > now() - INTERVAL 1 DAY ORDER BY total_duration_ms DESC LIMIT 50"}')"
Then read the physical plan of the slow statements for the "Photon does not support" seam and the ColumnarToRow / RowToColumnar boundaries — the detection recipe and the UDF-rewrite fixes are in ${CLAUDE_SKILL_DIR}/references/photon-eligibility-and-fallback.md.
Before bumping the runtime, run the two pre-upgrade detectors against the job's code and libraries:
bash# D03 — writes to the CWD that the DBR-14 500 MB workspace-FS cap will break: python3 "${CLAUDE_SKILL_DIR}/scripts/find-cwd-writes.py" --risk-only path/to/job/ # D04 — JARs built for a pre-17 JDK that DBR 15.1's JDK 17 may reject at runtime: bash "${CLAUDE_SKILL_DIR}/scripts/scan-jar-jdk.sh" path/to/libs/
Cross-reference each hop's landmines (the 14.x CWD cap, the 15.1 DBFS-root-library and JDK-11 removals, the 15.4 JDBC calendar flip) in ${CLAUDE_SKILL_DIR}/references/dbr-upgrade-paths.md.
If a job keeps aborting after NODES_LOST / SPOT_INSTANCE_TERMINATION around a shuffle, read the cluster's aws_attributes (clusters_get) and check the driver-on-demand rule and the spot ratio against ${CLAUDE_SKILL_DIR}/references/spot-vs-ondemand-decision.md. The #1 fix is pinning the driver (and a floor of workers) to on-demand so a spot reclaim can never take the driver.
provisioning / init-scripts / spark-startup, the dominant stage named, and the layer to investigate (or, for a failed start, the terminal code + its cause).
cluster-event-investigator) — the singlemost-likely cause with the specific events/codes that point to it, and the cause classes ruled out.
with the plan seam and the UDF-rewrite fix.
the JARs built for a pre-17 JDK, each with its line/file, plus the per-hop breaking-change notes.
aws_attributes (driver on-demand,spot ratio) for the job class.
| Error | Cause | Solution | |-------|-------|----------| | NPIP_TUNNEL_SETUP_FAILURE / CLOUD_PROVIDER_LAUNCH_FAILURE | One of five sub-causes (IP exhaustion, DNS, NSG, deleted VNet, throttling) | Disambiguate via termination-codes.md; the fix differs per sub-cause — do not blanket-retry. | | clusters_events empty or truncated | Databricks prunes old events | Note the truncation; a missing INIT_SCRIPTS_FINISHED may mean "pruned", not "hung" — do not infer an init-script hang from absence alone. | | Workspace MCP not registered | Connector not set up | Advisory mode: accept a pasted clusters.events JSON and run the forensics script on it. | | Photon audit returns nothing | No system.query.history grant, or DATABRICKS_WAREHOUSE_ID unset | Confirm the warehouse id and the system.query grant chain; degrade to reading a pasted query plan. | | scan-jar-jdk.sh reports JDK ? | JAR has no class files, or unzip missing | Install unzip; a JDK ? means the JAR is resources-only (no bytecode to check). | | Cold-start script says "unmeasured" for a stage | The boundary events are absent (no init scripts, or pruned events) | Expected — the script never folds an unmeasured stage into another; investigate the measured stages. |
Step 1 buckets the events: provisioning 21m (84%), init-scripts 1m, spark-startup 3m. Dominant is provisioning → the skill points at cloud VM allocation / subnet-IP / DNS, not init scripts, and loads termination-codes.md for the provisioning sub-causes to check.
The investigator subagent runs its threads; the network/NPIP thread owns it and disambiguates to "custom DNS could not resolve the control-plane hostname" (vs the other four causes), citing the exact check from termination-codes.md.
Step 3 runs find-cwd-writes.py (flags 3 to_parquet("staging/…") writes at risk under the 14.x cap) and scan-jar-jdk.sh (flags 2 JARs built for JDK 11), and dbr-upgrade-paths.md surfaces the 15.4 JDBC calendar flip for the pipeline's pre-Gregorian date handling.
Step 4 reads aws_attributes, finds the driver is on spot, and recommends first_on_demand covering the driver + a worker floor with SPOT_WITH_FALLBACK, citing the shuffle-recompute cascade in spot-vs-ondemand-decision.md.
${CLAUDE_SKILL_DIR}/references/termination-codes.md — codebook for every termination_reason.code, with the five-cause launch-failure umbrella.${CLAUDE_SKILL_DIR}/references/dbr-upgrade-paths.md — per-hop DBR breaking changes (14.x CWD cap, 15.1 lib/JDK removals, 15.4 calendar flip).${CLAUDE_SKILL_DIR}/references/photon-eligibility-and-fallback.md — what drops Photon to Spark and how to detect the premium-without-speedup.${CLAUDE_SKILL_DIR}/references/spot-vs-ondemand-decision.md — the spot config decision tree + driver-on-demand rule.${CLAUDE_SKILL_DIR}/scripts/cluster-coldstart-forensics.py — buckets cold-start PENDING time by stage.${CLAUDE_SKILL_DIR}/scripts/find-cwd-writes.py — AST scanner for DBR-14 CWD writes.${CLAUDE_SKILL_DIR}/scripts/scan-jar-jdk.sh — JAR bytecode-target (JDK) scanner.${CLAUDE_SKILL_DIR}/agents/cluster-event-investigator.md — parallel root-cause fanout subagent.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | fail→pass | 15,519 | 9,844 | -37% | 1 | 1 | 0% | 2,793 | 5,206 | +86% | 0 | 0 | — |
case-07 | fail→fail | 11,989 | 15,722 | +31% | 1 | 1 | 0% | 2,104 | 6,305 | +200% | 0 | 0 | — |
case-08 | fail→pass | 10,859 | 9,428 | -13% | 1 | 1 | 0% | 1,860 | 5,346 | +187% | 0 | 0 | — |
case-01 | fail→fail | 12,109 | 8,016 | -34% | 1 | 1 | 0% | 2,132 | 4,211 | +98% | 0 | 0 | — |
case-02 | fail→fail | 26,500 | 7,957 | -70% | 1 | 1 | 0% | 4,904 | 4,330 | -12% | 0 | 0 | — |
case-03 | fail→fail | 26,407 | 23,635 | -10% | 1 | 1 | 0% | 4,637 | 7,721 | +67% | 0 | 0 | — |
case-04 | fail→pass | 18,896 | 15,381 | -19% | 1 | 1 | 0% | 3,656 | 6,808 | +86% | 0 | 0 | — |
case-05 | fail→pass | 13,868 | 6,922 | -50% | 1 | 1 | 0% | 2,927 | 4,888 | +67% | 0 | 0 | — |
case-09 | fail→fail | 13,286 | 7,695 | -42% | 1 | 1 | 0% | 2,103 | 4,917 | +134% | 0 | 0 | — |
case-10 | pass→pass | 10,621 | 9,504 | -11% | 1 | 1 | 0% | 1,930 | 5,134 | +166% | 0 | 0 | — |
case-11 | pass→pass | 27,425 | 11,918 | -57% | 1 | 1 | 0% | 5,225 | 5,837 | +12% | 0 | 0 | — |
case-12 | pass→pass | 12,859 | 10,073 | -22% | 1 | 1 | 0% | 2,329 | 5,507 | +136% | 0 | 0 | — |
case-13 | fail→pass | 13,011 | 3,620 | -72% | 1 | 1 | 0% | 2,172 | 4,209 | +94% | 0 | 0 | — |
case-14 | pass→pass | 8,161 | 10,499 | +29% | 1 | 1 | 0% | 1,372 | 5,415 | +295% | 0 | 0 | — |
case-15 | fail→pass | 13,236 | 6,433 | -51% | 1 | 1 | 0% | 2,318 | 4,827 | +108% | 0 | 0 | — |
case-16 | fail→pass | 15,411 | 9,538 | -38% | 1 | 1 | 0% | 2,698 | 5,362 | +99% | 0 | 0 | — |
case-17 | pass→pass | 18,247 | 20,249 | +11% | 1 | 1 | 0% | 2,758 | 6,592 | +139% | 0 | 0 | — |
case-18 | fail→pass | 10,841 | 2,402 | -78% | 1 | 1 | 0% | 2,041 | 4,022 | +97% | 0 | 0 | — |
case-19 | pass→pass | 18,436 | 18,129 | -2% | 1 | 1 | 0% | 3,301 | 6,932 | +110% | 0 | 0 | — |
case-20 | pass→pass | 12,362 | 14,726 | +19% | 1 | 1 | 0% | 2,470 | 6,712 | +172% | 0 | 0 | — |
case-21 | pass→pass | 9,837 | 9,473 | -4% | 1 | 1 | 0% | 1,929 | 5,356 | +178% | 0 | 0 | — |
case-22 | pass→pass | 11,501 | 7,645 | -34% | 1 | 1 | 0% | 2,073 | 5,007 | +142% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 20 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 20 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.