{"record":{"id":"34b3aac02f6ba0a3","repo":"aeron-io/aeron","slug":"failed-to-send-invalidate-recording-request","errorCode":null,"errorMessage":"failed to send invalidate recording request","messagePattern":"failed to send invalidate recording request","errorType":"exception","errorClass":"ArchiveException","httpStatus":null,"severity":"error","filePath":"aeron-archive/src/main/java/io/aeron/archive/client/AeronArchive.java","lineNumber":1722,"sourceCode":"     * Purge a stopped recording, i.e. mark recording as {@link io.aeron.archive.codecs.RecordingState#INVALID}\n     * and delete the corresponding segment files. The space in the Catalog will be reclaimed upon compaction.\n     *\n     * @param recordingId of the stopped recording to be purged.\n     * @return count of deleted segment files.\n     */\n    public long purgeRecording(final long recordingId)\n    {\n        lock.lock();\n        try\n        {\n            ensureConnected();\n            ensureNotReentrant();\n\n            lastCorrelationId = aeron.nextCorrelationId();\n\n            if (!archiveProxy.purgeRecording(recordingId, lastCorrelationId, controlSessionId))\n            {\n                throw new ArchiveException(\"failed to send invalidate recording request\");\n            }\n\n            return pollForResponse(lastCorrelationId);\n        }\n        finally\n        {\n            lock.unlock();\n        }\n    }\n\n    /**\n     * List active recording subscriptions in the archive. These are the result of requesting one of\n     * {@link #startRecording(String, int, SourceLocation)} or a\n     * {@link #extendRecording(long, String, int, SourceLocation)}. The returned subscription id can be used for\n     * passing to {@link #stopRecording(long)}.\n     *\n     * @param pseudoIndex       in the active list at which to begin for paging.\n     * @param subscriptionCount to get in a listing.","sourceCodeStart":1704,"sourceCodeEnd":1740,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-archive/src/main/java/io/aeron/archive/client/AeronArchive.java#L1704-L1740","documentation":"AeronArchive throws this when the client could not offer a purgeRecording request onto the archive control publication. ArchiveProxy.purgeRecording returns false when the publication offer fails (session closed or back-pressured after retries), so the request never reached the archive. It indicates a transport/control-session problem, not a problem with the recordingId itself.","triggerScenarios":"Calling AeronArchive.purgeRecording(recordingId) while the control session is no longer connected, or when the control response/publication is back-pressured and the offer fails after ArchiveProxy retries. Also raised if the archive has closed the session (e.g. timeout, archive shutdown).","commonSituations":"Archive process restarted between connect and purge; control channel misconfigured so the publication never connects; slow archive leaving the exclusive control publication back-pressured; using an AeronArchive whose underlying session was closed by another thread.","solutions":["Check archive connectivity before the call: verify ctx.controlResponseChannel/StreamId and that the archive is running on the configured control channel.","Wrap the operation with retry: catch ArchiveException, call context().close() and AeronArchive.connect() again, then retry purgeRecording once the session is re-established.","Inspect archive logs for session timeout/close; increase archive control session timeout if sessions expire during long operations.","Verify network reachability of the control channel (firewall, interface binding) and that the archive's control-channel counter tracks the same endpoint.","If the offer is persistently back-pressured, reduce load or use a dedicated control session (AeronArchive.Configuration control channel tuning)."],"exampleFix":"// before\narchive.purgeRecording(recordingId);\n// after\nif (!archive.context().controlSessionId() != 0 && archive.context().isClosed()) {\n    archive = AeronArchive.connect(archive.context());\n}\ntry {\n    archive.purgeRecording(recordingId);\n} catch (ArchiveException e) {\n    if (e.message().contains(\"failed to send\")) {\n        archive.close();\n        archive = AeronArchive.connect(ctx);\n        archive.purgeRecording(recordingId);\n    } else {\n        throw e;\n    }\n}","handlingStrategy":"try-catch","validationCode":"if (archive == null || archive.context().isClosed()) { archive = AeronArchive.connect(ctx); }","typeGuard":"static boolean isSendFailure(ArchiveException e) { return e.getMessage() != null && e.getMessage().startsWith(\"failed to send\"); }","tryCatchPattern":"try { archive.purgeRecording(recordingId); } catch (ArchiveException e) { if (isSendFailure(e)) { archive = AeronArchive.connect(ctx); archive.purgeRecording(recordingId); } else { throw e; } }","preventionTips":["Keep the AeronArchive session alive with periodic lightweight calls if it is long-lived and mostly idle.","Recreate the AeronArchive after any archive restart or network reconfiguration.","Never share one AeronArchive across threads; it is guarded as non-reentrant and misuse can corrupt session state.","Monitor Aeron error logs and control publication counters for back-pressure/disconnects before they surface as this error."],"tags":["aeron-archive","control-session","ipc-offer-failed","network"],"backgroundTag":"api-request-failed","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}