Problem
When a subprocess analyzer exits 0 but writes no analysis.json, the SDK raises
CodeanalyzerExecutionException: codeanalyzer-typescript did not generate analysis.json.
and discards the analyzer's own diagnostic, which is the only thing that says why. The run is captured (capture_output=True) and then thrown away.
Hit for real on a whole-repo TypeScript analysis: the analyzer printed
[codeanalyzer-ts] FATAL TypeError: Type error
at structuredClone (unknown)
at finalizeAnalysis (...)
to stderr and exited 0. The SDK's message named none of it. Diagnosing it meant re-running the binary by hand outside the SDK — which is exactly the work the exception exists to save.
Where
Both subprocess backends, same shape:
cldk/analysis/typescript/codeanalyzer/codeanalyzer.py:232-236 — the CompletedProcess is not even bound on this path.
cldk/analysis/java/codeanalyzer/codeanalyzer.py:290-294 — console_out is bound and then unused in the message.
The Python backend is unaffected: it calls codeanalyzer-python in process, so there is no captured stream to lose.
The check=True path (:222, :280) is fine — CalledProcessError's str() carries the command and the return code, though not the streams either.
Scope boundary
In scope: carry a bounded tail of the analyzer's stderr (and stdout when stderr is empty) into the raised message, on both subprocess backends.
Out of scope: the upstream defects that produced the case — codeanalyzer-typescript#180 (the crash) and #181 (exit 0 on FATAL). The SDK cannot rely on those being fixed; an analyzer that fails in a new way should still be diagnosable from the exception.
Goals
Caveats and known risks
- The output can be large; a tail bound is required, and it must be applied to bytes actually read, not to a promise about analyzer behaviour.
- Analyzer output can contain absolute paths from the user's machine. That is already true of every other message the SDK raises, so no new exposure — but do not widen it to full stdout by default.
Definition of done
- Running either subprocess backend against an analyzer that exits 0 without writing
analysis.json yields an exception naming both the missing file and what the analyzer said, and a test pins it.
Problem
When a subprocess analyzer exits 0 but writes no
analysis.json, the SDK raisesand discards the analyzer's own diagnostic, which is the only thing that says why. The run is captured (
capture_output=True) and then thrown away.Hit for real on a whole-repo TypeScript analysis: the analyzer printed
to stderr and exited 0. The SDK's message named none of it. Diagnosing it meant re-running the binary by hand outside the SDK — which is exactly the work the exception exists to save.
Where
Both subprocess backends, same shape:
cldk/analysis/typescript/codeanalyzer/codeanalyzer.py:232-236— theCompletedProcessis not even bound on this path.cldk/analysis/java/codeanalyzer/codeanalyzer.py:290-294—console_outis bound and then unused in the message.The Python backend is unaffected: it calls
codeanalyzer-pythonin process, so there is no captured stream to lose.The
check=Truepath (:222,:280) is fine —CalledProcessError'sstr()carries the command and the return code, though not the streams either.Scope boundary
In scope: carry a bounded tail of the analyzer's
stderr(andstdoutwhenstderris empty) into the raised message, on both subprocess backends.Out of scope: the upstream defects that produced the case — codeanalyzer-typescript#180 (the crash) and #181 (exit 0 on FATAL). The SDK cannot rely on those being fixed; an analyzer that fails in a new way should still be diagnosable from the exception.
Goals
stderrtail, truncated to a sane bound, for both Java and TypeScriptCalledProcessError's bare command line is not the whole storyCaveats and known risks
Definition of done
analysis.jsonyields an exception naming both the missing file and what the analyzer said, and a test pins it.