NTSB findings

What NTSB found in 24,558 investigations, coded in a tree of 5 categories, 21 subcategories, 130 sections and 792 subsections. NTSB marks the findings that are part of the probable cause (a cause or contributing factor); the rest are other findings. NTSB publishes the codes’ names but not their definitions.

By category

Cases with a finding in each category, per year: in green, those with one in the probable cause. Findings are coded with the final report, often a year or more after the event, so the latest years are incomplete.

Aircraft
20082026
Cases with a finding in Aircraft, per year: 19,175 cases
20081,297
20091,286
20101,161
20111,213
20121,206
20131,003
20141,047
20151,034
20161,075
20171,060
20181,065
20191,048
2020921
2021950
20221,010
2023975
2024924
2025637
2026263

19,176 cases · 18,374 in the probable cause

Personnel issues
20082026
Cases with a finding in Personnel issues, per year: 18,967 cases
20081,113
20091,219
20101,155
20111,262
20121,264
20131,025
20141,068
20151,038
20161,051
20171,072
20181,079
20191,051
2020908
2021938
2022999
2023918
2024872
2025655
2026280

18,967 cases · 18,419 in the probable cause

Environmental issues
20082026
Cases with a finding in Environmental issues, per year: 10,285 cases
2008933
2009669
2010591
2011564
2012566
2013507
2014612
2015568
2016665
2017700
2018663
2019613
2020441
2021463
2022428
2023437
2024423
2025310
2026132

10,285 cases · 5,684 in the probable cause

Not determined
20082026
Cases with a finding in Not determined, per year: 2,247 cases
2008150
2009199
2010195
2011156
2012171
2013128
2014150
2015163
2016137
2017135
2018143
2019118
202086
202198
202276
202368
202437
202528
20269

2,247 cases · 2,176 in the probable cause

Organizational issues
20082026
Cases with a finding in Organizational issues, per year: 504 cases
200847
200937
201045
201137
201249
201333
201439
201533
201624
201724
201822
201915
20207
202112
202221
202322
202429
20256
20262

504 cases · 388 in the probable cause

Query with SQL
duckdb -csv -c "SELECT left(f.\"findingCode\", 2) AS category, max(f.\"tier1Name\") AS name, year(c.\"eventDate\") AS year,
    count(DISTINCT f.case_mkey) AS cases,
    count(DISTINCT f.case_mkey) FILTER (WHERE f.\"inProbableCause\") AS in_probable_cause
  FROM 'https://openairdocs.org/data/latest/ntsb_findings.parquet' f JOIN 'https://openairdocs.org/data/latest/ntsb_case_detail.parquet' c ON c.\"mKey\" = f.case_mkey
  WHERE c.\"eventDate\" >= DATE '2008-01-01' AND f.\"findingCode\" GLOB '[0-9][0-9]*'
  GROUP BY 1, 3
  ORDER BY 1, 3"

Prints the counts as CSV. It reads the release’s Parquet files over HTTPS, so there’s nothing to download first. Install DuckDB to run it.

Most often in the probable cause

Query with SQL
duckdb -csv -c "-- The page charts the top 5 by in_probable_cause, and by fatal_cases
  SELECT left(f.\"findingCode\", 8) AS finding, max(f.\"tier4Name\") AS name, max(f.\"tier1Name\") AS category,
    count(DISTINCT f.case_mkey) AS cases,
    count(DISTINCT f.case_mkey) FILTER (WHERE f.\"inProbableCause\") AS in_probable_cause,
    count(DISTINCT f.case_mkey) FILTER (WHERE c.\"highestInjuryLevel\" = 'Fatal') AS fatal_cases
  FROM 'https://openairdocs.org/data/latest/ntsb_findings.parquet' f JOIN 'https://openairdocs.org/data/latest/ntsb_case_detail.parquet' c ON c.\"mKey\" = f.case_mkey
  WHERE f.\"findingCode\" GLOB '[0-9][0-9]*'
  GROUP BY 1
  ORDER BY in_probable_cause DESC, cases DESC"

Prints the counts as CSV. It reads the release’s Parquet files over HTTPS, so there’s nothing to download first. Install DuckDB to run it.

Every finding

The whole tree, with the cases under each code. Open a level to see the one below.

5 categories, 21 subcategories, 130 sections and 792 subsections
Query with SQL
duckdb -csv -c "SELECT l.level, left(f.\"findingCode\", l.level * 2) AS code,
    max([f.\"tier1Name\", f.\"tier2Name\", f.\"tier3Name\", f.\"tier4Name\"][l.level]) AS name,
    count(DISTINCT f.case_mkey) AS cases,
    count(DISTINCT f.case_mkey) FILTER (WHERE f.\"inProbableCause\") AS in_probable_cause,
    count(DISTINCT f.case_mkey) FILTER (WHERE c.\"highestInjuryLevel\" = 'Fatal') AS fatal_cases
  FROM 'https://openairdocs.org/data/latest/ntsb_findings.parquet' f JOIN 'https://openairdocs.org/data/latest/ntsb_case_detail.parquet' c ON c.\"mKey\" = f.case_mkey CROSS JOIN range(1, 5) AS l(level)
  WHERE f.\"findingCode\" GLOB '[0-9][0-9]*'
  GROUP BY 1, 2
  ORDER BY code"

Prints the counts as CSV. It reads the release’s Parquet files over HTTPS, so there’s nothing to download first. Install DuckDB to run it.

NTSB has coded findings and defining events since 2008. Earlier cases used its older occurrence and cause codes, so they aren’t counted here.