Updated 9/6/2026: The PowerShell scripts in this post have been rewritten and tested end to end. The original versions captured the decoder with 2>&1, which fails silently in any PowerShell script that sets $ErrorActionPreference = 'Stop': the decoder’s stderr output becomes a terminating error, the call throws, and if your loop catches it you get no rows and no warning. Every script below now starts the decoder with System.Diagnostics.Process and reads StandardError directly, which is immune to that setting. Two more corrections: rawdata-t2.pb is not miscellaneous runtime data, it holds the drawing name (RP/Dwg), the last command chain, the user email, the machine name and the AppCData block with the managed stack, so merge all the .pb files in a folder before you look for anything. And the GPU key is AI/d3d/Description, not AdapterDescription. Full details and the reasoning are in CER Schema Update: The Data My Parser Missed, and every key is documented on the v7 .pb format reference.
If you’ve been managing or using Autodesk products for a few years, you’ve probably cracked open a CER file at some point. Customer Error Reporting is Autodesk’s built-in crash reporting system, and it’s been around for a long time. When Civil 3D, AutoCAD, or Revit goes down hard — an unplanned exit — CER packages up what it knows and sends it off. Hardware info, loaded modules, driver versions, last commands, the document being worked on, the faulting code. The whole picture.
For many years that data lived in an XML file. You could open it in Notepad, search for GPU, and have a pretty good idea of what happened in under five minutes. Not fancy. But it worked. That changed with the 2025 product line.
A Common Misconception
Most people assume CER data is just for Autodesk. You submit it, it disappears into their systems, and hopefully a future update fixes whatever broke. That’s how a lot of users think about it. That’s not the whole story.
The crash files live locally on the machine before they ever get uploaded, and there’s a registry key that controls how many are stored on the system. That means you can read them too. And when you do, you stop guessing. I’ve had some real wins in the last few months tracing crashes back to specific hardware configurations, environment issues, customization conflicts, and bad DWG files causing unplanned exits. CER has become my favorite first stop when something doesn’t add up, when a user is reporting repeated crashes, or when I spot something on my radar in a dashboard.
I liked it enough that I submitted sessions on using CER to solve crashes and lost productivity for Autodesk University 2026. The data is genuinely useful beyond individual troubleshooting — it’s a way to build a picture of how your Autodesk users are doing across the entire company, including current update versions and hardware driver health fleet-wide.
What Changed with CER v7
CER v7 replaced XML with PB files. Protocol Buffers. Binary format. Open one in a text editor now and you get noise. The first time most CAD managers ran into this it wasn’t a great moment.
The reasoning behind the change is solid. XML files were growing, Autodesk was capturing more crash detail, and binary storage is smaller and faster. Integration with Windows Error Reporting also improved, which means better capture rates on crashes the older system sometimes missed. Real infrastructure work. It just broke every workflow users had built around opening that XML file.
Depending on the crash, you may also see a .dmp memory dump alongside the .pb, or multiple .pb files depending on the types of data captured. Two file types, neither of them readable out of the box.
One other change worth knowing: starting with the 2025 products, CER data is sent automatically. Before that, users saw a dialog after a crash and had to choose whether to submit. That dialog is gone now. Reporting happens in the background. For enterprise environments that’s actually good news — your crash data is more complete, and you’re not relying on users to click submit in a moment of frustration.
Where the Files Live
Files land locally before upload at:
C:\Users\Username\AppData\Local\Autodesk\CER
Each crash creates a folder with two levels of subfolders — a machine hash and a timestamp. Inside that folder you will find up to three .pb files:
- rawdata-t1.pb — crash identity, OS version, the native exception (code, text, faulting module), product inventory when present, and session counters
- rawdata-t2.pb — identity and payload: computer name, user email, the active drawing (
RP/Dwg), the last command chain, and the AppCData block with the managed stack and GDI counts - graphic-data-t1.pb — installed GPU, active D3D adapter, driver version, DirectX info, VRAM, and the DxDiag system block. Not present in every crash folder
Decode all of them and merge the keys. The fields you care about most are split across the files, and the second file is the one that carries the drawing name.
Retention Setting
Default retention is five crash reports per product. If you’re supporting more than a handful of machines that’s not enough. Change that number before you need it. Add a REG_DWORD value named MaxRetainedCount at:
HKEY_CURRENT_USER\Software\Autodesk\SendMiniDmp\Settings
The default value is 5. Set it higher across your fleet via Group Policy or Intune before the next major crash wave.
The Tool That Unlocks the Files
Once you know the .pb file exists and where to find it, the next question is how to actually open it. That’s where cer_rawdataviewer.exe comes in. This is Autodesk’s own viewer tool that ships with the 2025 products. You’re not downloading a third-party utility or finding something on a forum — it’s already on the machine at:
C:\Program Files\Autodesk\Autodesk CER\service\cer_rawdataviewer.exe
The tool takes a .pb file as input and converts it to JSON you can actually read. No admin rights required.
Two things to know before you start. The decoder writes its output to stderr, not stdout, and the output begins with a timestamp line that has to be stripped before the JSON can be parsed. From a plain Command Prompt a 2>&1 redirect handles the first part fine. From PowerShell it does not, at least not reliably: in a script that sets $ErrorActionPreference = 'Stop', PowerShell turns each stderr line into an error record and the redirect throws, so your loop gets nothing. The safe way is to start the process yourself and read its error stream directly. Every PowerShell method below does that through one small function.
Confirm the tool is present first:
Test-Path "C:\Program Files\Autodesk\Autodesk CER\service\cer_rawdataviewer.exe"
Returns True: ready to go. Returns False: the machine does not have a 2025+ product installed.
Method 1 — Command Prompt
The simplest approach. Good for a quick one-off inspection of a single file. Open Command Prompt (no elevation needed) and run:
"C:\Program Files\Autodesk\Autodesk CER\service\cer_rawdataviewer.exe" "C:\path\to\rawdata-t1.pb" > C:\Temp\output.json 2>&1
Open C:\Temp\output.json in VS Code or any text editor. Delete the first line (the timestamp prefix). The remainder is clean JSON. This is cmd.exe, not PowerShell, so the redirect problem described above does not apply here.
The Decoder Function — Paste This First
Every PowerShell method below calls this function. Paste it into your session, or at the top of your script, before running Methods 2 through 4 or the bulk collector. It starts the decoder as a child process, reads stderr directly, strips the timestamp prefix, and returns the JSON as a string, or $null if the decoder produced no JSON.
function Decode-CerPb {
param(
[string]$PbFile,
[string]$Viewer = "C:\Program Files\Autodesk\Autodesk CER\service\cer_rawdataviewer.exe"
)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $Viewer
$psi.Arguments = '"' + $PbFile + '"'
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $true
$psi.RedirectStandardError = $true
$psi.RedirectStandardOutput = $true
$proc = [System.Diagnostics.Process]::Start($psi)
$stdErr = $proc.StandardError.ReadToEnd()
$stdOut = $proc.StandardOutput.ReadToEnd()
$proc.WaitForExit()
$raw = if ($stdErr) { $stdErr } else { $stdOut } # the JSON is on stderr
$jsonStart = $raw.IndexOf('{')
if ($jsonStart -lt 0) { return $null }
return $raw.Substring($jsonStart)
}
Reading the process streams directly bypasses PowerShell’s error stream entirely, so $ErrorActionPreference never gets a chance to interfere. If the function returns $null the decoder ran but emitted no JSON, which usually means the .pb file is empty or damaged, not that the decode failed.
Method 2 — PowerShell, Single File to JSON
Decodes one .pb file and saves clean JSON.
$pbFile = "$env:LOCALAPPDATA\Autodesk\CER\HASH_FOLDER\TIMESTAMP_FOLDER\rawdata-t1.pb"
# Replace HASH_FOLDER and TIMESTAMP_FOLDER with the actual folder names on your machine.
# Not sure what they are? Run this first:
# Get-ChildItem "$env:LOCALAPPDATA\Autodesk\CER" -Recurse -Filter "*.pb" | Select-Object FullName
$outFile = "C:\Temp\rawdata-t1.json"
$json = Decode-CerPb $pbFile
if ($json) {
$json | Out-File $outFile -Encoding utf8 -Force
Write-Host "Saved to $outFile"
} else {
Write-Host "No JSON returned for $pbFile"
}
Method 3 — PowerShell, All .pb Files in a Crash Folder
Decodes every .pb file under the CER root into separate JSON files. Use this when you want to inspect rawdata-t1, rawdata-t2 and graphic-data from the same crash side by side.
$cerRoot = "$env:LOCALAPPDATA\Autodesk\CER"
$outDir = "C:\Temp\CER_Extracted"
New-Item -Path $outDir -ItemType Directory -Force | Out-Null
Get-ChildItem -Path $cerRoot -Filter "*.pb" -Recurse | ForEach-Object {
$json = Decode-CerPb $_.FullName
if ($json) {
$outFile = Join-Path $outDir ($_.BaseName + ".json")
$json | Out-File $outFile -Encoding utf8 -Force
Write-Host "Saved: $outFile"
} else {
Write-Host "No JSON returned for: $($_.Name)"
}
}
Output files are named to match their source: rawdata-t1.json, rawdata-t2.json, graphic-data-t1.json. If you are decoding more than one crash folder at a time, add the folder name to the output file name so the files do not overwrite each other.
Method 4 — PowerShell, Auto-Find Most Recent Crash
Don’t know the hash or timestamp folder name? This finds the most recently modified crash automatically and decodes all of its .pb files.
$cerRoot = "$env:LOCALAPPDATA\Autodesk\CER"
$outDir = "C:\Temp\CER_Extracted"
New-Item -Path $outDir -ItemType Directory -Force | Out-Null
$crashFolder = Get-ChildItem -Path $cerRoot -Recurse -Filter "*.pb" -File |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 |
ForEach-Object { $_.DirectoryName }
Write-Host "Decoding crash folder: $crashFolder"
Get-ChildItem -Path $crashFolder -Filter "*.pb" | ForEach-Object {
$json = Decode-CerPb $_.FullName
if ($json) {
$outFile = Join-Path $outDir ($_.BaseName + ".json")
$json | Out-File $outFile -Encoding utf8 -Force
Write-Host "Saved: $outFile"
}
}
Bulk Collection: All .pb Files to JSON
Once you have the syntax confirmed, the real value is processing files in bulk. This script finds every .pb file in the CER folder, runs each one through the decoder, and saves the JSON output to a folder you can review or feed into something else. It reports how many files produced no JSON so a silent failure cannot hide.
$cerRoot = "$env:LOCALAPPDATA\Autodesk\CER"
$outputDir = "C:\CEROutput"
if (-not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir | Out-Null
}
$pbFiles = Get-ChildItem -Path $cerRoot -Filter "*.pb" -Recurse
if ($pbFiles.Count -eq 0) { Write-Host "No .pb files found."; exit }
$saved = 0
$empty = 0
foreach ($file in $pbFiles) {
Write-Host "Processing: $($file.FullName)"
$json = Decode-CerPb $file.FullName
if ($json) {
$outputFile = Join-Path $outputDir ($file.BaseName + ".json")
$json | Out-File $outputFile -Encoding utf8 -Force
Write-Host " Saved: $outputFile"
$saved++
} else {
Write-Host " WARNING: No JSON output for $($file.Name)"
$empty++
}
}
Write-Host "Done. $saved JSON files saved to $outputDir, $empty files produced no JSON."
Compiling Crashes into a CSV
JSON isn’t your only option. You can compile the key fields from every crash folder into a single CSV — which opens a fast path into Power BI or Excel. This script is standalone: it includes its own copy of the decoder function, handles both the pre-2025 XML and the 2025+ .pb formats, and merges all the .pb files in a folder before reading any keys, which is what makes the drawing name and the faulting module show up.
# ------------------------------------------------------------
# Autodesk CER Export - Blog Example (revised 9/6/2026)
# Supports pre-2025 XML (dmpuserinfo.xml) and 2025+ PB files
# Outputs a CSV ready for Power BI or AI analysis
# ------------------------------------------------------------
$CerRoot = "X:\Misc\AutodeskCER"
# Replace X:\Misc\AutodeskCER with the root folder where you
# collect CER folders from your machines. Each subfolder should
# be a username, containing crash subfolders with .xml or .pb files.
$Output = "C:\Temp\CER_Export.csv"
$CerViewer = "C:\Program Files\Autodesk\Autodesk CER\service\cer_rawdataviewer.exe"
function Decode-CerPb {
param([string]$PbFile, [string]$Viewer)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $Viewer
$psi.Arguments = '"' + $PbFile + '"'
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $true
$psi.RedirectStandardError = $true
$psi.RedirectStandardOutput = $true
$proc = [System.Diagnostics.Process]::Start($psi)
$stdErr = $proc.StandardError.ReadToEnd()
$stdOut = $proc.StandardOutput.ReadToEnd()
$proc.WaitForExit()
$raw = if ($stdErr) { $stdErr } else { $stdOut } # the JSON is on stderr
$jsonStart = $raw.IndexOf('{')
if ($jsonStart -lt 0) { return $null }
return $raw.Substring($jsonStart)
}
function Get-PbParam {
param ($Params, [string]$Key)
if ($null -eq $Params) { return "" }
$entry = $Params[$Key]
if ($null -eq $entry) { return "" }
$vals = $entry.value
if ($null -eq $vals -or $vals.Count -eq 0) { return "" }
return ($vals -join "; ")
}
function Get-PbParams {
# Decodes every .pb in the folder and merges the keys into one table.
# graphic-data-t1.pb is processed last so it never overwrites a crash key.
param ([string]$CrashFolder, [string]$ViewerExe)
$merged = @{}
if (-not (Test-Path $ViewerExe)) { return $merged }
$pbFiles = Get-ChildItem -Path $CrashFolder -Filter "*.pb" -ErrorAction SilentlyContinue |
Sort-Object { if ($_.Name -eq "graphic-data-t1.pb") { 1 } else { 0 } }
foreach ($pb in $pbFiles) {
$json = Decode-CerPb -PbFile $pb.FullName -Viewer $ViewerExe
if (-not $json) { continue }
$parsed = $json | ConvertFrom-Json -ErrorAction SilentlyContinue
if (-not $parsed -or -not $parsed.params) { continue }
$parsed.params.PSObject.Properties | ForEach-Object {
if (-not $merged.ContainsKey($_.Name)) { $merged[$_.Name] = $_.Value }
}
}
return $merged
}
function Get-XmlAttr {
param ($Node, [string]$Attr)
if ($Node -and $Node.Attributes[$Attr]) {
return $Node.Attributes[$Attr].Value
}
return ""
}
function Get-OpenFiles {
param ($UserInfoNode)
$files = @()
if ($UserInfoNode) {
foreach ($child in $UserInfoNode.ChildNodes) {
if ($child.Name -match '\.(dwg|dwt|dxf)$') {
$name = Get-XmlAttr $child "name"
if ($name) { $files += $name }
}
}
}
return ($files -join "; ")
}
$Results = Get-ChildItem -Path $CerRoot -Recurse -Directory | ForEach-Object {
$CrashFolder = $_.FullName
$XmlFile = Join-Path $CrashFolder "dmpuserinfo.xml"
$PbFiles = Get-ChildItem -Path $CrashFolder -Filter "*.pb" -ErrorAction SilentlyContinue
$hasXml = Test-Path $XmlFile
$hasPb = ($PbFiles -and $PbFiles.Count -gt 0)
if (-not $hasXml -and -not $hasPb) { return }
$relative = $CrashFolder.Substring($CerRoot.Length + 1)
$username = $relative.Split('\')[0]
$refFile = if ($hasXml) { $XmlFile } else {
($PbFiles | Sort-Object LastWriteTime | Select-Object -First 1).FullName
}
$crashDateTime = (Get-Item $refFile).LastWriteTime
$row = [ordered]@{
Username = $username
CrashDateTime = $crashDateTime
FormatVersion = if ($hasXml -and $hasPb) { "XML+PB" } elseif ($hasXml) { "XML" } else { "PB" }
ComputerName = ""
ProductName = ""
ProductVersion = ""
OSVersion = ""
GPUModel = ""
GPUDriver = ""
FaultingModule = ""
ExceptionCode = ""
LastCommand = ""
OpenDWGFiles = ""
}
if ($hasXml) {
try {
[xml]$xml = Get-Content $XmlFile -ErrorAction Stop
$userInfo = $xml.SelectSingleNode("//UserInfo")
if ($userInfo) {
$row.ComputerName = Get-XmlAttr $userInfo "ComputerName"
$row.OpenDWGFiles = Get-OpenFiles $userInfo
$wd = $userInfo.SelectSingleNode("WorkingDocument")
if ($wd -and -not $row.OpenDWGFiles) { $row.OpenDWGFiles = Get-XmlAttr $wd "name" }
}
$appInfo = $xml.SelectSingleNode("//AppInformation")
if ($appInfo) {
$row.ProductName = Get-XmlAttr $appInfo "name"
$row.ProductVersion = Get-XmlAttr $appInfo "version"
}
$osInfo = $xml.SelectSingleNode("//OSInfo")
if ($osInfo) {
$major = Get-XmlAttr $osInfo "MajorVersion"
$minor = Get-XmlAttr $osInfo "MinorVersion"
$build = Get-XmlAttr $osInfo "BuildNumber"
$row.OSVersion = "$major.$minor (Build $build)"
}
$gpu = $xml.SelectSingleNode("//DxDiagInfo/GraphicsDeviceInfo")
if ($gpu) {
$chipType = $gpu.SelectSingleNode("ChipType")
$driverVer = $gpu.SelectSingleNode("DriverFileVersion")
if ($chipType) { $row.GPUModel = $chipType.InnerText.Trim() }
if ($driverVer) { $row.GPUDriver = $driverVer.InnerText.Trim() }
}
# The last command and managed exceptions live in the AppCDATA block.
$appCData = $xml.SelectSingleNode("//AppCDATA")
if ($appCData -and $appCData.InnerText -match '(?m)^Command:\s*([^;\r\n]+)') {
$row.LastCommand = $Matches[1].Trim()
}
if ($appCData -and $appCData.InnerText -match "-----Last-0 'first chance' exception:\s*\r?\n([^\r\n]+)") {
# Keep the exception TYPE only, so this column stays comparable to the PB hex code.
$row.ExceptionCode = ($Matches[1] -split ':\s')[0].Trim()
}
} catch {
Write-Warning "XML parse error in $XmlFile : $_"
}
}
if ($hasPb) {
$params = Get-PbParams -CrashFolder $CrashFolder -ViewerExe $CerViewer
if ($params.Count -gt 0) {
# Identity: UPI first, then the AppInformation fragment in RP/AppXML.
$pbProduct = Get-PbParam $params "SP/UPI_PRODUCT"
$pbBuild = Get-PbParam $params "SP/UPI_BUILD"
if (-not $pbProduct) {
$appXml = Get-PbParam $params "RP/AppXML"
if ($appXml -match '<AppInformation\s+name="([^"]+)"(?:\s+version="([^"]*)")?') {
$pbProduct = $Matches[1]
if (-not $pbBuild -and $Matches.Count -gt 2) { $pbBuild = $Matches[2] }
}
}
if (-not $pbBuild) { $pbBuild = Get-PbParam $params "SP/UPI_RELEASE" }
if ($pbProduct -and -not $hasXml) { $row.ProductName = $pbProduct }
if ($pbBuild -and -not $hasXml) { $row.ProductVersion = $pbBuild }
if (-not $row.OSVersion) {
$major = Get-PbParam $params "AI/os/MajorVersion"
$minor = Get-PbParam $params "AI/os/MinorVersion"
$build = Get-PbParam $params "AI/os/BuildNumber"
$row.OSVersion = "$major.$minor (Build $build)"
}
# Active adapter from AI/d3d, installed GPU from AI/dx.graphic as fallback.
if (-not $row.GPUModel) { $row.GPUModel = Get-PbParam $params "AI/d3d/Description" }
if (-not $row.GPUModel) { $row.GPUModel = Get-PbParam $params "AI/dx.graphic/ChipType" }
if (-not $row.GPUDriver) { $row.GPUDriver = Get-PbParam $params "AI/d3d/DriverVersion" }
if (-not $row.GPUDriver) { $row.GPUDriver = Get-PbParam $params "AI/dx.graphic/DriverFileVersion" }
# The native exception block: XML never had this.
$row.FaultingModule = Get-PbParam $params "AP/exception/module_name"
$pbCode = Get-PbParam $params "AP/exception/code"
if ($pbCode) { $row.ExceptionCode = $pbCode }
# Drawing and command live in rawdata-t2.pb under RP/ keys.
if (-not $row.OpenDWGFiles) { $row.OpenDWGFiles = Get-PbParam $params "RP/Dwg" }
if (-not $row.LastCommand) {
$chain = Get-PbParam $params "RP/LastCommand" # multi-value, newest first
if ($chain) { $row.LastCommand = ($chain -split ';')[0].Trim() }
}
if (-not $row.LastCommand) {
$cdata = Get-PbParam $params "RP/AppCData"
if ($cdata -match '(?m)^Command:\s*([^;\r\n]+)') { $row.LastCommand = $Matches[1].Trim() }
}
if (-not $row.ComputerName) { $row.ComputerName = Get-PbParam $params "AI/os/ComputerName" }
}
$userinfoFile = Get-ChildItem -Path $CrashFolder -Filter "*.userinfo" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($userinfoFile) {
try {
$ui = Get-Content $userinfoFile.FullName -Raw | ConvertFrom-Json
if ($ui.computer_name) { $row.ComputerName = $ui.computer_name }
} catch {}
}
}
[pscustomobject]$row
}
$Results | Sort-Object CrashDateTime | Export-Csv $Output -NoTypeInformation -Force
# Wrap each filter in @(): in Windows PowerShell 5.1 a single-match result has no .Count
# and the total would print blank.
$xmlCount = @($Results | Where-Object { $_.FormatVersion -eq "XML" }).Count
$pbCount = @($Results | Where-Object { $_.FormatVersion -eq "PB" }).Count
$mixCount = @($Results | Where-Object { $_.FormatVersion -eq "XML+PB" }).Count
Write-Host ""
Write-Host "Export complete: $Output"
Write-Host "Total records : $(@($Results).Count)"
Write-Host " XML (pre-2025) : $xmlCount"
Write-Host " PB (2025+) : $pbCount"
Write-Host " XML+PB (both) : $mixCount"
A few notes on what changed from the original version of this script. The decoder is started as a process, not captured with a redirect. The .pb files in a folder are merged before any key is read, with graphic-data-t1.pb last so it cannot overwrite a crash key. Product identity falls back to the AppInformation fragment in RP/AppXML when there is no UPI block, which is common. The GPU comes from AI/d3d/Description with AI/dx.graphic/ChipType as a fallback. The drawing comes from RP/Dwg, and the last command from RP/LastCommand or the Command: line in RP/AppCData. On the XML side the last command now comes from the AppCDATA block rather than the rarely present RuntimeInformation element, and folders holding both formats are labeled XML+PB with the XML treated as authoritative for identity.
I ran this against a folder holding a 2024 XML crash, a full 2026 PB crash, a thin startup PB crash, and a folder carrying both formats, with $ErrorActionPreference set to Stop the whole time. All four parsed. The XML rows returned the product, GPU and drawing; the full PB row returned dswhip.dll, 0xC0000005, REGEN and the drawing name; the thin PB row correctly returned the product and GPU with the runtime fields blank; and the mixed folder took its identity from the XML while picking up the faulting module from the PB. Two things that run turned up, both now fixed above: the XML exception column was holding the entire exception message rather than the type, and the summary counts printed blank when exactly one row matched, because a single object has no .Count in Windows PowerShell 5.1.
Taking It Further: Power BI and AI Analysis
Pull that CSV into Power BI and you’ve got a live dashboard showing crash rates by product version, GPU driver distribution, and faulting module frequency across your entire user base. Connect it to a shared folder that updates regularly and the dashboard stays current without manual work.
The other path worth knowing about is feeding the JSON or CSV directly into an AI tool for analysis. Claude and ChatGPT are both solid at this. What feels like a haystack with no needle often has a clear signal buried in it — a driver version showing up disproportionately before faults, a specific module that keeps appearing across machines in the same office, a pattern that only surfaces after a recent update rollout. Drop your CSV into a conversation, describe what you’re managing, and ask it to look for clusters and outliers. You’ll often walk away with a lead you wouldn’t have found manually.
Once you’re pulling CER data consistently, the picture starts coming together fast:
- Hardware dashboard — GPU models, driver versions, and memory configs across your fleet.
- Software version tracking — who’s on what update, and whether crash rates shift after a rollout.
- Crash rate trends — are things getting better or worse after a change you made?
- Cluster analysis — the same faulting module across multiple machines, the same drawing triggering failures on different workstations, a plugin appearing right before a fault.
Single crashes are noise. Clusters are signal. When the same pattern shows up across users, departments, or machines, you’re not troubleshooting anymore — you’re diagnosing. And you can show your work. That changes the conversation with vendors, with IT, and with leadership.
I’ve traced crashes back to specific customization conflicts, data issues, and environment problems that never would have surfaced through a support ticket or call alone. CER data got me there faster almost every time.
The PB to JSON conversion adds a step to a workflow that used to be simpler. But the data quality is better, the capture rate is better, and once you have the process down it’s not much friction. Build a consistent review habit and start treating this data like the diagnostic tool it actually is.
Key Fields Reference
Once decoded, every .pb file exposes its data under a params object. Each key maps to a value array. Here are the most useful fields. The complete list, every key in all three files with how reliably each one appears, is on the v7 .pb format reference.
rawdata-t1.pb:
AI/CrashDate -- Date the crash occurred
AI/CrashUUID -- Unique identifier for this crash
AI/os/BuildNumber -- Windows build number
AI/os/MajorVersion -- Windows major version
AP/exception/code -- Exception code (e.g. 0xC0000005)
AP/exception/text -- Exception type (e.g. ACCESS_VIOLATION)
AP/exception/module_name -- DLL or module where the crash occurred
CD/SubscriptionMacID -- Autodesk subscription machine ID
SP/UPI_PRODUCT -- Product code (e.g. CIV3D, ACD)
SP/UPI_BUILD -- Product build (e.g. 13.8.1809.0) - use this for versions
SP/UPI_RELEASE -- Release year only (e.g. 2025)
SP/UPI_FULL_XML -- Full installed product inventory as XML
RP/SessionStartCount -- Sessions started on this machine
RP/SessionEndCount -- Sessions closed cleanly
RP/LastCommand -- Command chain, most recent first (multi-value)
rawdata-t2.pb:
AI/os/ComputerName -- Machine name
RP/Dwg -- The active drawing at crash
RP/UserSubscriptionEmail -- User email
RP/AppXML -- AppInformation / ProductInformation XML fragments
RP/AppCData -- Command line, managed stack, GDI counts (same as the XML AppCDATA)
RP/GraphicsDriver -- AutoCAD hdi driver (e.g. acaddm17.hdi)
graphic-data-t1.pb:
AI/d3d/Description -- Active D3D adapter (the GPU the app rendered on)
AI/d3d/DriverVersion -- Active adapter driver version
AI/dx.graphic/ChipType -- Installed GPU per DxDiag
AI/dx.graphic/DriverFileVersion -- Installed GPU driver version
AI/dx.graphic/DriverDate -- Installed GPU driver date
AI/dx.graphic/GraphicsHWMemory -- GPU VRAM (e.g. "32169 MB")
AI/dx.system/SystemModel -- Machine model (e.g. Precision 5690)
GPU fields only appear when a graphic-data-t1.pb file is present in the crash folder. Not every crash generates one. When AI/d3d/Description names one adapter and AI/dx.graphic/ChipType names another, the app was rendering on a different GPU than the one you think is installed, which is the hybrid graphics signature.
-Shaan



