{"record":{"id":"72236c6d30a845b5","repo":"apache/flink","slug":"write-record-failed","errorCode":null,"errorMessage":"Write record failed","messagePattern":"Write record failed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/common/io/OutputFormatBase.java","lineNumber":143,"sourceCode":"                    if (throwable == null) {\n                        callback.onSuccess(result);\n                    } else {\n                        callback.onFailure(throwable);\n                    }\n                });\n    }\n\n    /**\n     * Send the actual record for writing.\n     *\n     * @return a CompletionStage that represents the writing task.\n     */\n    protected abstract CompletionStage<V> send(OUT record);\n\n    private void checkAsyncErrors() throws IOException {\n        final Throwable currentError = throwable.getAndSet(null);\n        if (currentError != null) {\n            throw new IOException(\"Write record failed\", currentError);\n        }\n    }\n\n    /** Close the format waiting for pending writes and reports errors. */\n    @Override\n    public final void close() throws IOException {\n        checkAsyncErrors();\n        flush();\n        checkAsyncErrors();\n        postClose();\n    }\n\n    /**\n     * Tear down the OutputFormat. This method is called at the end of {@link\n     * OutputFormatBase#close()}.\n     */\n    protected void postClose() {}\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/common/io/OutputFormatBase.java#L125-L161","documentation":"Thrown by OutputFormatBase.checkAsyncErrors as an IOException wrapping the first asynchronous write error captured in an AtomicReference. The base class records failures from send()/flush() completions and re-throws them synchronously on the next checkAsyncErrors call (in close(), and around flush()). This turns silent async failures into visible exceptions at lifecycle boundaries.","triggerScenarios":"A subclass of OutputFormatBase implements send(record) returning a CompletionStage that completes exceptionally (e.g. network error, rejected record, serialization failure). The error is stored; the next checkAsyncErrors (called from close, or between writes) wraps it in IOException('Write record failed', cause) and rethrows.","commonSituations":"Sink/OutputFormat writing to a database, queue, or HTTP endpoint where the async send fails: connection drop, auth expiry, schema rejection, timeout, full disk on flush. Errors may lag behind the records that caused them, surfacing only at close().","solutions":["Inspect the wrapped cause in the IOException to find the real failure (network, auth, schema, disk).","Make send() retry transient errors (with backoff) so only permanent failures propagate to checkAsyncErrors.","Ensure flush() is called periodically so errors surface before the final close, and add error-handling/retry around close().","Add monitoring on the async write path so failures are visible immediately rather than only at close."],"exampleFix":"// before: send fails and error only surfaces in close()\nprotected CompletionStage<Void> send(OUT r) { return client.write(r); }\n// after: retry transient failures inside send\nprotected CompletionStage<Void> send(OUT r) {\n  return client.write(r).exceptionallyCompose(\n      e -> isTransient(e) ? retryWithBackoff(() -> client.write(r)) : failedFuture(e));\n}","handlingStrategy":"try-catch","validationCode":"// Before close(), drain pending writes and surface errors early\nformat.flush();              // triggers checkAsyncErrors internally\n// in a periodic loop while writing:\nformat.flush();              // surfaces async failures before the final close","typeGuard":null,"tryCatchPattern":"try {\n    format.close();\n} catch (IOException e) {\n    Throwable cause = e.getCause();\n    if (cause instanceof TransientException) {\n        // retry the failed batch if the format supports replay\n        retryBatch();\n    } else {\n        throw e; // permanent failure\n    }\n}","preventionTips":["Implement retry-with-backoff inside send() for transient errors so only permanent failures reach checkAsyncErrors.","Call flush() periodically so async errors surface before close().","Inspect the wrapped cause (IOException.getCause()) to diagnose the real failure.","Add metrics/alerts on the async write path so failures are visible immediately."],"tags":["output-format","async","io","error-handling"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}