Vala async retry pattern: re-invoking a GError-throwing method without duplicating writes
0 reputation · 14 Jul 2026, 08:11 UTC
0 reputation · 14 Jul 2026, 08:11 UTC
I have a Vala async method (compiled against Vala 0.44+ / GLib GTask) that performs a write, e.g. appending a record to a file or sending a request. On failure it throws a GError, which the caller catches via try/catch around the yield. I want to retry the operation a bounded number of times, but only when the write provably did not happen, so a retry never produces a duplicate record.
Since GTask finalizes its resources when it completes, each retry presumably needs a fresh invocation of the async method rather than reusing any task state. What is unclear is the error semantics: when the async method throws, there is no documented guarantee about how far the underlying operation progressed. The error may have been raised before the write, after a partial write, or after a complete write whose success signal was lost.
The compiler does not enforce anything about shared mutable state between attempts, so idempotency has to come from my own design (sequence numbers, unique keys, or pre-write checks).
GError from a yield and simply calling the same async method again the intended retry pattern, or are there pitfalls in how continuations and the main context are scheduled across attempts?31775 reputation · 14 Jul 2026, 18:54 UTC
Yes – the standard way to retry a Vala async method that may throw a GError is to wrap each yield in a try/catch block and, on catching a transient error, simply yield the same async method again. Each call creates a fresh GLib.Task, so no state is reused and the continuation is scheduled sequentially on the main context.
GLib.Task cannot be restarted; therefore each retry must invoke the async method anew.GError only supplies domain, code and message; there is no built‑in guarantee about whether the underlying write happened, was partial, or never started.yield and the main loop runs other sources before resuming.max_attempts = 3).attempt < max_attempts:yield obj.write_async(...) in try/catch (GLib.Error e).e.domain and e.code. Retry only for error codes that your API documents as transient (e.g. network timeouts, temporary lock failures).yield, break the loop.yield GLib.timeout_add_seconds_async(backoff_seconds, () => { return false; }) (or use a timeout source via GLib.MainContext).To decide whether a caught GError is safe to retry, you need to know which specific error domains/codes your write operation treats as transient. If you can share those codes (or confirm that all errors from this method are transient), the recommendation can be finalized; otherwise you must filter retries to the known transient set.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.