Stream Errors and Diagnostics

This page is a draft.

The stream API separates bounded flow-control results from exceptional I/O and encoding failures. This page shows how to branch on normal results, inspect StreamError, render diagnostics, retain source context, and handle malformed text separately.

Separate Results from Failures

Timeout and Finished are result states. Timeout gives control back to the caller without changing the stream into Failed; finished marks normal input completion. Native read, write, flush, close, or positioning failures throw StreamError.

/// Keep timeout, end-of-stream, and failure on separate control-flow paths.
/// Timeout is a bounded result, `Finished` is a successful end state, and a stream failure throws `StreamError`.
void distinguishResultStates() {
    auto input = ScriptedByteInputStream{{7U}, 1U, 1U};
    el::io::printLine("Tiempo agotado: "_el, input.readByte().isTimeout());
    el::io::printLine("Dato marino: "_el, input.readByte().hasData());
    el::io::printLine("Fin normal: "_el, input.readByte().isFinished());
}
Tiempo agotado: true
Dato marino: true
Fin normal: true

Inspect Structured Context

StreamError contains StreamErrorContext. Its title states what failed; its description explains why. Optional help suggests recovery, path identifies a file-backed source, and platform context carries native details without nesting a second cause.

/// `StreamError` carries a structured, user-facing context.
/// Handlers can inspect its title, description, recovery help, path, and optional native platform context.
void inspectStreamError() {
    auto input = ScriptedByteInputStream{{1U}};
    input.setFailure(true);
    try {
        static_cast<void>(input.readByte());
    } catch (const el::StreamError &error) {
        el::io::printLine("Title: "_el, error.title());
        el::io::printLine("Description: "_el, error.description());
        el::io::printLine("Estado Failed: "_el, input.state() == el::StreamState::Failed);
    }
}
Title: Failed to read the scripted stream.
Description: The simulated sensor source failed.
Estado Failed: true

Report the Common Diagnostic

Use diagnostic() or DiagnosticHelper to transform the exception into the same renderer-neutral document used by application errors. This keeps terminal output, logs, and graphical reporting consistent.

/// Convert a stream exception into the common diagnostic document used by logs and user interfaces.
/// This avoids constructing a second message and preserves the original stream-domain context.
void reportDiagnostic() {
    auto output = ScriptedByteOutputStream{};
    output.setFailure(true);
    try {
        output.flush();
    } catch (const el::StreamError &error) {
        const auto document = el::DiagnosticHelper{error}.toDocument();
        el::io::printLine(document.toString());
    }
}
Error: Failed to flush the scripted stream.
  The simulated recorder failed.

Preserve Context Through Wrappers

Text encoders, temporary streams, and standard proxies delegate context creation to the backing stream’s StreamErrorSource before adding their own context data. The final exception therefore retains a useful file path and native details even when failure was detected at a decorated text operation.

/// Encoded and temporary stream wrappers forward locally created errors to their backing stream.
/// As a result, a text-level operation can still report the path associated with its file-backed byte source.
void preserveWrappedContext() {
    const auto directory = createStreamDemoDirectory("océano"_el);
    const auto path = directory->path() / "arrecife.txt"_el;
    const auto output = path.content().openTextOutputStream();
    output->write("coral"_el);
    output->close();
    try {
        static_cast<void>(output->write("pez"_el));
    } catch (const el::StreamError &error) {
        el::io::printLine("Ruta conservada: "_el, error.path().endsWith("arrecife.txt"_el));
    }
}
Ruta conservada: true

Handle Encoding Errors Separately

Strict decoding throws EncodingError because malformed text is not a native stream failure. Catch it separately when the application can explain invalid input differently from an unavailable file or device.

/// Encoding failures are text-domain errors rather than stream failures.
/// Configure strict decoding when malformed marine-observation text must be rejected and handle `EncodingError`
/// separately from native I/O failures.
void handleEncodingError() {
    const auto directory = createStreamDemoDirectory("codificación"_el);
    const auto path = directory->path() / "muestra.txt"_el;
    path.content().writeDataOrThrow(el::ByteBlock({0xf0U, 0x28U, 0x8cU, 0x28U}));
    auto options = el::PathReadTextOptions{el::StringEncoding::Utf8};
    options.setEncodingMode(el::EncodingMode::Strict);

    try {
        static_cast<void>(path.content().openTextInputStream(options)->readAll());
    } catch (const el::EncodingError &) {
        el::io::printLine("The malformed UTF-8 sequence was rejected."_el);
    }
}
The malformed UTF-8 sequence was rejected.

After a stream enters Failed, report its original error and replace the stream only at an application-level recovery boundary. Do not retry arbitrary requests on the failed instance.