# security scanner agent's SARIF report failed to parse: finding triage stalled

## TL;DR
The SARIF is malformed or uses a shape your parser doesn't expect. Validate it against the SARIF schema, normalize the common deviations (null results, missing driver info), and only then feed it to triage.

```text
security scanner agent's SARIF report failed to parse: finding triage stalled
```

## Use this when
- An agent's security triage stalls on a parse error instead of findings
- The scanner wrote a file but the parser rejects it
- A scanner version upgrade changed the output shape
- GitHub code scanning or another strict consumer rejects the upload

## Not for this skill when
- The scanner ran fine and produced zero findings (that's a scope question)
- The format isn't SARIF at all
- The file is fine but the triage logic itself is wrong

## Steps
1. Check the file is actually JSON and complete; truncated writes are the top cause:
```
python3 -c "import json; d=json.load(open('results.sarif')); print(d.get('version'), len(d.get('runs', [])))"
```
Expected: prints something like `2.1.0 1`. A JSON decode error means the write was truncated; re-run the scanner.

2. Validate against the SARIF JSON schema (install the jsonschema package if needed):
```
python3 -c "
import json, jsonschema
schema = json.load(open('sarif-schema.json'))
doc = json.load(open('results.sarif'))
jsonschema.validate(doc, schema)
print('valid')
"
```
Expected: prints "valid", or a precise error path like runs[0].results telling you what's wrong.

3. Normalize the deviations parsers choke on: null results become an empty list, a missing tool driver name gets a fallback:
```
python3 -c "
import json
d = json.load(open('results.sarif'))
for run in d.get('runs', []):
    if run.get('results') is None:
        run['results'] = []
    drv = run.setdefault('tool', {}).setdefault('driver', {})
    drv.setdefault('name', 'unknown-scanner')
json.dump(d, open('results.fixed.sarif', 'w'), indent=2)
print('normalized')
"
```
Expected: prints "normalized", and your triage parser now reads the file.

4. Count findings per rule to confirm the triage input is sane:
```
python3 -c "
import json
from collections import Counter
d = json.load(open('results.fixed.sarif'))
c = Counter(r.get('ruleId', 'no-rule') for run in d['runs'] for r in run['results'])
print(len(c), 'distinct rules')
print(c.most_common(5))
"
```
Expected: a plausible rule count, not zero and not millions.

### Variant: GitHub code scanning rejects the upload
It enforces the schema strictly. Fix at the source rather than normalizing after the fact; normalized files are for your tooling, the upload wants the real thing.

### Variant: SARIF from a custom tool missing the version field
Add "version": "2.1.0" and the schema pointer. Strict parsers require both.

### Variant: huge SARIF files
Stream-parse instead of loading it all into memory. The structure is the same; the reading strategy changes.

## Why this happens
SARIF is a big spec and every scanner emits a slightly different dialect: null vs empty arrays, optional fields omitted, extensions in odd places. Strict parsers reject what lenient ones accept, so the agent's triage stalls on a parse error instead of a security question.

## Edge cases and pitfalls
- Don't silently drop results during normalization; log what you changed.
- If the scanner version changed recently, diff its output against the last good one before blaming your parser.
- Keep the original file; normalized copies are for tooling, the original is the audit record.
- An empty results list is valid SARIF; don't treat "zero findings" as a parse failure.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_WDL5XJBQUbcgOsSri07HVg
