What Happens on Generation Reservation Failure: Error Handling in awesome-gpt-image-2

When an image generation reservation fails, the system automatically rolls back the user's usage credits by invoking the release_generation_reservation Supabase RPC with an appropriate error code, ensuring no charges are incurred for failed or incomplete requests.

In the freestylefly/awesome-gpt-image-2 repository, the image generation pipeline uses a reservation system to manage user credits. When upstream providers like Apimart fail to complete a request, the codebase implements a robust error handling mechanism that detects failures, translates error codes, and releases reservations to restore the user's credit balance.

Detecting Generation Failures in the API Handler

The primary failure detection occurs in api/generate-image.js. The code wraps the upstream submission in a try-catch block that intercepts any errors from submitApimartGeneration.

When an exception occurs, the catch block (lines 61-75) immediately initiates the rollback process:

} catch (error) {
  const errorCode = publicErrorCode(error);
  await releaseReservation(auth.client, reservation.reservationId, errorCode);
  // ...
}

This ensures that no pending reservation remains active if the generation request cannot be fulfilled.

Mapping Private Errors to Public Error Codes

Before releasing the reservation, the system normalizes error messages through the publicErrorCode function (lines 66-71). This abstraction prevents leaking internal provider details while categorizing failures:

export function publicErrorCode(error) {
  if (error?.code === 'APIMART_RATE_LIMITED') return 'UPSTREAM_BUSY';
  // ...
  return 'GENERATION_FAILED';
}

Supported error mappings include:

  • APIMART_RATE_LIMITED → UPSTREAM_BUSY
  • Default fallback → GENERATION_FAILED

Releasing the Reservation and Restoring Credits

The releaseReservation function (lines 51-58) performs the actual rollback by invoking a Supabase RPC. This updates the reservation status to released and records the failure reason:

async function releaseReservation(client, reservationId, errorCode) {
  if (!reservationId) return;
  const { error } = await client.rpc('release_generation_reservation', {
    p_reservation_id: reservationId,
    p_error_code: errorCode || 'GENERATION_FAILED'
  });
  // ...
}

The release_generation_reservation stored procedure restores the user's credit balance and marks the reservation as released, making the failure state visible to the client.

Handling Failures in Provider Callbacks

An alternative failure path exists in api/generation/callback.js for asynchronous provider callbacks. When the provider calls the /api/generation endpoint, the code first validates the reservation status (lines 29-31):

const reservation = await findReservation(client, taskId);
if (!reservation) return { state: 'ignored' };
if (reservation.status !== 'pending') return { state: 'duplicate' };
await settle(client, reservation, verifiedTask);

If the reservation is not in pending status, the callback is treated as a duplicate. Otherwise, the system proceeds to settlement.

Settlement Logic and Final State Resolution

The settlePlatformGeneration function in api/_lib/generation.js (lines 71-84) handles the final state transition. If the task status is anything other than completed, it releases the reservation:

if (task.status === 'completed' && task.image) {
  // complete reservation
} else {
  await client.rpc('release_generation_reservation', {
    p_reservation_id: reservation.id,
    p_error_code: task.errorCode || 'GENERATION_FAILED'
  });
}

This ensures that failed, cancelled, or errored tasks all trigger the credit rollback mechanism.

Summary

Key takeaways from the generation reservation failure handling:

  • Automatic rollback: Every failure path calls release_generation_reservation to restore user credits
  • Error normalization: Internal provider errors are mapped to public codes like UPSTREAM_BUSY or GENERATION_FAILED
  • Dual protection: Both synchronous try-catch blocks and asynchronous callback handlers implement release logic
  • Idempotent handling: The callback system prevents duplicate processing by checking reservation status before settlement
  • Transparency: Error codes are stored with the reservation, allowing clients to understand why generation failed

Frequently Asked Questions

What error codes does the system return on generation reservation failure?

The system returns normalized error codes such as UPSTREAM_BUSY for rate limiting or GENERATION_FAILED for generic errors. These are stored in the reservation record via the p_error_code parameter of the release_generation_reservation RPC, ensuring clients receive consistent error messaging regardless of the upstream provider's response.

How does the system ensure user credits are restored after a failure?

Credits are restored through the release_generation_reservation Supabase RPC call, which is triggered by either the releaseReservation function in the main handler or the settlePlatformGeneration function in the callback processing. Both pathways update the reservation status to released and revert the credit deduction made during reservation creation.

What happens if the provider sends a duplicate callback for a failed generation?

In api/generation/callback.js, the system checks the reservation status before processing. If the status is not pending, the callback returns { state: 'duplicate' } and ignores the request. This prevents double-releasing reservations or incorrectly updating user credit balances for already-settled transactions.

Which files contain the core failure handling logic?

The primary failure detection resides in api/generate-image.js, while api/_lib/generation.js contains the settlement logic used by both synchronous and asynchronous paths. The callback-specific handling is implemented in api/generation/callback.js, and the database operations are defined in the Supabase migration files that create the release_generation_reservation RPC.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →