Repository navigation
Marten 9.31.2
A diagnostics-only patch release. No behavioural change to the write path.
A failed batched write now reports the SQL that failed
When a batched write failed, Marten threw a MartenCommandException whose message rendered an empty command text. Reported from the field on an async projection:
Marten Command Failure:$ $ $ 42601: syntax error at end of input POSITION: 56
Error trying to build and apply changes to event subscription MyProjection:All
The $ $ $ is the message template interpolating a command that was always null. ReadNpgsqlCommand() looks for an NpgsqlCommand in exception.Data, and on the ExecuteBatchPagesAsync path there is no single command to put there — AutoClosingLifetime transformed the exception with nothing recorded at all, and WrapAndThrow(NpgsqlBatch, ...) recorded the batch under a key that nothing ever read.
The practical consequence: the offending SQL was unrecoverable from the exception and from the logs, for every async projection write. That is precisely the situation where hand-written SQL — a QueueSqlCommand, a custom IStorageOperation, a projection side effect — turns out to be malformed, and the one thing you need to see is the statement.
MartenCommandException now recovers the SQL from three sources, in order:
- the
NpgsqlCommandit was handed; NpgsqlException.BatchCommand— Npgsql identifies the exact statement the server rejected when the failure came out of a batch execution;- an
NpgsqlBatchrecorded on the exception.
The recovered statement is also exposed on a new MartenCommandException.CommandText property, so it can be read programmatically rather than scraped out of the message.
AutoClosingLifetime executes its batches inline rather than through handleCommandException, so it now records its batch. The other three connection lifetimes already routed through WrapAndThrow(NpgsqlBatch, ...) and only needed a reader for the key they were already writing.
Two guardrails worth knowing about: a batch renders at most five statements, so a 500-operation projection page cannot turn one failure into an unreadable log entry, and the whole resolution is wrapped in a catch — building a diagnostic must never replace the real failure.
Also released as 8.38.1 on the 8.x line.