CNA-GAP-001: ContentLoadException derives from std::runtime_error, not System::Exception, so no Sharp Runtime catch clause spans CNA's XNA exceptions
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
Unlike XNA, where every framework exception is a System.Exception, CNA's ContentLoadException (deliberately) derives from std::runtime_error, so catch (const System::Exception&) misses content-load failures; only std::exception spans the framework.
- Identifier
CNA-GAP-001- Category
- Functional gap
- Subsystem
- Core & runtime
- Status
- Narrowed (partially fixed; describes only what survives)
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::Content::ContentLoadException and the catchability of CNA exceptions through System::Exception
Expected behaviour
In XNA 4.0 ContentLoadException derives from System.Exception like every other framework exception, so a port that catches Exception around Content.Load expects the C++ equivalent, catch (const System::Exception&), to see it.
Actual behaviour at TARGET
At TARGET the XNA-named exceptions of graphics (DeviceLostException, DeviceNotResetException, NoSuitableGraphicsDeviceException), audio, gamer services, net, devices and the content pipeline derive from System::Exception. ContentLoadException in ContentLoadException.hpp derives from std::runtime_error, and its header states that this is deliberate ("because a great deal of code catches std::runtime_error around content loads"); it stores its own inner cause because that base has none. The CNA-specific CNA::Platform::PlatformException also derives from std::runtime_error, while CNA::CNAException derives from System::Exception. Sharp Runtime's System::Exception derives from std::exception, not std::runtime_error (Sharp Runtime next at 41b918c9, not pinned by TARGET), and the math module throws standard exception types (see the exception-families entry).
So a handler for System::Exception does not see content-load failures, a handler for std::runtime_error does not see the System::Exception family, and only catch (const std::exception&) spans the framework.
Fixed since the earlier record: the three graphics exceptions used to derive from std::runtime_error; they now derive from System::Exception and GraphicsExceptionTests.cpp pins that. What remains is a documented, deliberate narrowing, not a defect.
Source locations
modules/content/include/Microsoft/Xna/Framework/Content/ContentLoadException.hpp— class ContentLoadException : public std::runtime_error, with the rationale in getInnerExceptionProperty's commentmodules/graphics/include/Microsoft/Xna/Framework/Graphics/DeviceLostException.hpp— now derives from System::Exception (the fixed part)modules/platform/include/CNA/Platform/PlatformException.hpp— PlatformException : std::runtime_errormodules/graphics/tests/Microsoft/Xna/Framework/Graphics/GraphicsExceptionTests.cpp— pins the System::Exception base of the three graphics exceptionsmodules/content/tests/Microsoft/Xna/Framework/Content/ContentRuntimeContractTests.cpp— ContentLoadExceptionContractTest.TheParameterlessConstructorNamesItsOwnType: catchable as std::runtime_errormodules/c-api/src/CnaCApiDetail.hpp— CallWithExceptionBarrier names ContentLoadException and PlatformException explicitly
Evidence
Checked by reading at 009d40f5; nothing was built or executed for this entry. The bases were read from every public exception header under modules/*/include; the deliberate choice is stated in the ContentLoadException header and pinned by ContentRuntimeContractTests. The graphics fix is pinned by GraphicsExceptionTests.
Focused reproduction
Illustrative; not compiled or run for this entry.
try { // inside a Game subclass
auto texture = getContentProperty().Load<Texture2D>("missing");
} catch (const System::Exception& e) {
// never reached for a missing asset: ContentLoadException is not a System::Exception
} catch (const std::exception& e) {
// reached
}
Current tests
GraphicsExceptionTests asserts that the graphics exceptions are caught as System::Exception and as std::exception; ContentRuntimeContractTests asserts that ContentLoadException is caught as std::runtime_error. No test documents that it is not a System::Exception.
Regression test
Not a defect to fix. If the base is ever changed to System::Exception, the std::runtime_error expectation in ContentRuntimeContractTests must change with it and every content throw site that is caught as runtime_error must be reviewed.
Blast radius
C++ code ported from XNA that catches Exception around content loads, and generic error handlers written against System::Exception. The C ABI is unaffected: its barrier maps ContentLoadException explicitly to CNA_RESULT_IO.
Workaround
Catch ContentLoadException explicitly around content loads, and use std::exception only at a top-level boundary.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.