An image-generation request can time out even after the provider has accepted the job.
That creates an awkward situation: the application has no result, but another generation may already be running. Automatically repeating the request can create a second paid job.
This article examines that boundary using the current provider adapter in Nano Banana 2.1, a project I work on. It describes implementation choices and remaining failure cases, not a claim that every recovery scenario is solved.
Separate submission from polling
The adapter exposes two operations:
-
submit()creates a prediction and returns its identifier. -
poll()reads the status of that existing prediction.
A simplified version of the interface looks like this:
interface ImageProvider {
submit(request: ImageRequest): Promise<{ taskId: string }>;
poll(taskId: string): Promise<
| { status: "pending" }
| { status: "completed"; imageUrl: string }
| { status: "failed"; error: string }
>;
}
This separation matters because the operations have different consequences.
Repeating a status request asks about the same job. Repeating submission may create another job.
The adapter performs a single creation request and validates that the response contains a usable prediction ID. It does not contain an automatic retry loop around that POST.
A timeout is an unknown outcome
Consider this sequence:
- The application sends a creation request.
- The provider accepts it.
- The connection fails before the application receives the prediction ID.
The application cannot safely infer that nothing happened.
Avoiding an automatic resubmission reduces duplicate-job risk, but it leaves a recovery problem: without the ID, the application cannot use its normal polling path.
A more complete recovery design would need a documented provider idempotency mechanism or another way to reconcile the original request. Those capabilities should be verified rather than assumed.
Validate successful responses too
A succeeded status is not enough by itself.
In this adapter, the output can be a URL string or an array. The code selects the first array entry when necessary, then checks that the result is an HTTPS URL.
Malformed output becomes an explicit error instead of an apparently successful generation with an unusable result.
That check is only basic validation. It does not prove the URL is reachable, contains a valid image, or has been copied into application storage.
Keep error details out of user records
Provider error bodies can contain prompts or reference-image URLs.
For failed predictions, the adapter keeps a small operational error containing the provider, processing stage, category, and failure code. It does not copy the provider's raw error body into the returned failure message.
This preserves enough information to classify the failure without unnecessarily duplicating user input.
Test the uncertainty boundary
Useful tests for this design include:
- A creation request fails without returning an ID: no automatic second POST.
- A creation response lacks a valid ID: polling does not begin.
- A prediction remains in progress: return
pending. - A prediction succeeds with malformed output: return an explicit error.
- A prediction fails: return a sanitized failure rather than the raw response.
The main design lesson is to distinguish “the request failed” from “the work never started.” In an asynchronous, paid workflow, those are different states.
Disclosure: This article was drafted with AI assistance using the project's current source code. The interface example is simplified for explanation.
Top comments (0)