Does HttpRequestRetry ensure idempotency for POST requests during transport failures?
0 reputation · 06 Dec 2025, 15:57 UTC
0 reputation · 06 Dec 2025, 15:57 UTC
When using the HttpRequestRetry plugin in Ktor (JVM), the client can be configured to retry requests based on server errors or specific exceptions. However, a critical uncertainty arises when a timeout or connection reset occurs after the server has already processed a write operation but before the client receives the response.
In this scenario, the client perceives a failure and triggers a retry. If the endpoint is a POST request, this behavior can lead to duplicate resource creation or redundant side effects unless the server-side logic explicitly handles deduplication.
While PUT and DELETE are defined as idempotent, POST operations are not. There is no built-in mechanism within the Ktor client plugin to automatically coordinate with the server to ensure a retry does not result in a duplicate write.
modifyRequest block be utilized to maintain a consistent idempotency key across multiple retry attempts?HttpRequestRetry prevents the client from retrying a POST request when the failure occurs after the request body has been fully dispatched?35275 reputation · 07 Dec 2025, 03:33 UTC
No. The HttpRequestRetry plugin in Ktor only decides whether to retry a request based on the configured retryIf lambda, maxRetries, and exception types. It has no built‑in knowledge of whether the server has already applied the side effect of a POST request. Therefore, a retry after a transport failure that occurs after the body has been sent can still trigger duplicate writes unless the application adds its own idempotency logic.
Idempotency-Key header and rely on server‑side deduplication.POST after the body is sent by configuring retryIf to return false for that method or by disabling retryOnException for POST.retryIf (lambda that receives HttpRequestData and Throwable) and maxRetries.POST as non‑retryable after the body has been sent.modifyRequest { request ->
request.headers["Idempotency-Key"] = UUID.randomUUID().toString()
}retryIf to exclude POST after the body is dispatched:
install(HttpRequestRetry) {
maxRetries = 3
retryIf { request, throwable ->
// Do not retry POST if the body has already been sent
request.method.value.uppercase() != "POST" || throwable !is IOException
}
}Does the request body use a repeatable data source (e.g., ByteArrayContent) or a non‑repeatable stream (e.g., InputStreamContent)? If the body is non‑repeatable, a retry will fail with an exception rather than silently duplicate the write, and the strategy might need to change.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.