CurriculumPythonInterpreter throws ArgumentNullException on unparseable Python version (CS8604) #673

Closed
opened 2026-07-17 18:20:18 +00:00 by spikerj · 1 comment
Owner

Found via CS8604 triage. CurriculumPythonInterpreter (test helper in SpikerSoft.Business.Tests) resolves a Python interpreter meeting the curriculum minimum. Both version checks call PythonVersionGate.MeetsMinimum(PythonVersionGate.ParseVersion(version), minimum) INLINE.

ParseVersion returns Version? — null when the interpreter's version banner is non-empty but unparseable. MeetsMinimum begins with ArgumentNullException.ThrowIfNull(actual), so a null parse result throws ArgumentNullException instead of the intended clear XunitException. That masks the real diagnostic (which explains how to point the override at a valid Python) with a confusing argument-null error.

The sibling PythonInterpreterProbeHostedService does this correctly: it captures ParseVersion into a variable and guards 'if (resolved is null) return;' before calling MeetsMinimum.

Fix: capture ParseVersion(version) and null-guard it at both sites (lines 65, 86). An override whose version can't be parsed is rejected (same as below-minimum); a probed candidate whose version can't be parsed is skipped rather than selected. Test-infra only, edge case (unparseable-but-non-empty banner); build clean. SonarQube rule external_roslyn:CS8604.

Found via CS8604 triage. CurriculumPythonInterpreter (test helper in SpikerSoft.Business.Tests) resolves a Python interpreter meeting the curriculum minimum. Both version checks call PythonVersionGate.MeetsMinimum(PythonVersionGate.ParseVersion(version), minimum) INLINE. ParseVersion returns Version? — null when the interpreter's version banner is non-empty but unparseable. MeetsMinimum begins with ArgumentNullException.ThrowIfNull(actual), so a null parse result throws ArgumentNullException instead of the intended clear XunitException. That masks the real diagnostic (which explains how to point the override at a valid Python) with a confusing argument-null error. The sibling PythonInterpreterProbeHostedService does this correctly: it captures ParseVersion into a variable and guards 'if (resolved is null) return;' before calling MeetsMinimum. Fix: capture ParseVersion(version) and null-guard it at both sites (lines 65, 86). An override whose version can't be parsed is rejected (same as below-minimum); a probed candidate whose version can't be parsed is skipped rather than selected. Test-infra only, edge case (unparseable-but-non-empty banner); build clean. SonarQube rule external_roslyn:CS8604.
Author
Owner

Resolved in spikersoft-backend PR #372 (merged to master). Captured the ParseVersion result and null-guarded it before MeetsMinimum at both sites, so an unparseable Python version banner no longer throws ArgumentNullException. Closing.

Resolved in spikersoft-backend PR #372 (merged to master). Captured the ParseVersion result and null-guarded it before MeetsMinimum at both sites, so an unparseable Python version banner no longer throws ArgumentNullException. Closing.
Sign in to join this conversation.