How to show upload status and recover from failed uploads in Bubble

Illustration of upload attempts in progress, completed, and needing retry

The answer users need

An upload needs at least two kinds of progress: transfer progress and business status. A browser can say it finished sending bytes, while the backend still needs to verify the stored object, validate its contents, and make it available to the right organization. If the interface collapses all of that into “uploaded,” users may see a success message for a file the application cannot safely use.

Use a small, explicit state model. Show “uploading” while bytes move, “checking file” while the backend verifies them, “available” only when the application has accepted the file, and a specific recovery message when the process fails. Keep the same upload ID across retries so the user can recover without creating duplicate files.

This is a conceptual workflow. Bubble2X’s customer-facing file workflow is planned and is not represented here as implemented. The example assumes a client portal with a Bubble interface and a server-side integration that stores files in Google Cloud Storage. If an app uses Bubble’s built-in uploader, some transport details differ, but the distinction between user feedback and verified business state still helps.

Define the state machine before designing the progress bar

Write the states and allowed transitions in plain language. A workable example is:

selected → authorized → uploading → checking → available
     ↘ cancelled       ↘ interrupted  ↘ rejected
                        ↘ expired      ↘ needs review
selected → authorized → uploading → checking → available
     ↘ cancelled       ↘ interrupted  ↘ rejected
                        ↘ expired      ↘ needs review
selected → authorized → uploading → checking → available
     ↘ cancelled       ↘ interrupted  ↘ rejected
                        ↘ expired      ↘ needs review

“Selected” means the browser has a local file but no server permission. “Authorized” means the service has checked the user and organization and reserved an upload ID. “Uploading” means a transfer is in progress. “Checking” means the transfer appears complete, but the service has not yet accepted the stored object. “Available” is the only state that should allow normal download or downstream processing. “Rejected” means validation or policy failed. “Needs review” covers ambiguous cases that automated reconciliation cannot safely resolve.

Do not infer “available” from a client-side progress bar reaching 100%. The browser may have finished its own write while the service has not inspected the object. Equally, a network timeout does not prove the upload failed. The object may exist in Cloud Storage even though the browser never saw the response. The service needs a way to reconcile those states before deciding whether to ask for another transfer.

For a Cloud Storage design, Google’s signed URL guide explains that temporary URLs provide time-limited access to a resource. A new grant may be needed after expiry. For resumable uploads, Google notes that a session URI also acts as an authentication token; handle it as carefully as a signed URL. Neither mechanism tells Bubble that a document is ready for a customer to use.

Show status where the user makes decisions

Put a status row next to each file in the portal, not only in a transient toast. An agency’s client may leave the page, reopen it later, or ask a colleague whether a report is ready. The row should include a recognizable display name, current state, last update time, and a next action. Avoid showing a temporary storage URL or internal object path. Those values are operational identifiers, not useful client-facing status.

Use wording tied to what the system knows:

State

Suggested message

Next action

Authorized

“Ready to upload. Keep this page open while the file is sent.”

Start transfer

Uploading

“Sending file…” with measured transfer progress if available

Wait or cancel

Checking

“File received. Checking it before it is available.”

Wait; do not reupload yet

Available

“File is available to your organization.”

View or continue workflow

Interrupted

“We lost the connection. Check status before retrying.”

Refresh status

Rejected

“This file could not be accepted.” with a safe reason

Choose a different file or contact support

Needs review

“We received an uncertain result and are checking it.”

Do not retry blindly

Do not promise an exact completion time unless the workflow has measured evidence for one. Avoid telling a user “nothing was uploaded” after a timeout unless a server check confirms no object exists. Keep technical errors in a support-visible event with a correlation ID; the user message should say what they can do.

Give every attempt an identity

When the user selects a file, the app can generate or request an idempotency key for the logical submission. The service creates one upload record with an opaque ID, organization ID derived from authorized context, expected object ID, timestamp, and policy snapshot. If Bubble sends the same authorization request again because the response was lost, the service should return the existing record or a clear conflict. It should not silently create a second customer document.

Use distinct IDs for the user-visible document and each transfer attempt if the policy allows multiple attempts. That makes it possible to say “attempt two completed the document” without losing the history of attempt one. A retry should be deliberate: verify whether the previous object exists, whether the temporary grant has expired, and whether a partial or complete object needs cleanup. The backend, not the browser, decides whether a new upload is safe.

Cloud Storage request preconditions can prevent an unintended overwrite when the chosen API operation supports them. A create-only condition can fail if an object already exists. That failure should be interpreted carefully: it may mean a duplicate retry after a successful first attempt, not necessarily an attacker. Look up the expected upload record and object metadata before presenting the user with a final result.

Separate transient failures from final rejection

A network interruption, temporary service error, or browser tab closure may justify retry or reconciliation. A file that violates the accepted type, size, or content policy should be rejected with a stable reason. A forbidden organization request should be denied and logged, not offered a retry button that repeats the same authorization failure. These distinctions help both users and operators.

For each failure, decide three things: whether bytes might already exist, whether the authorization grant is still valid, and whether the user can safely retry. If the browser disconnected during transfer, the backend should check storage for the exact object before issuing a second grant. If validation failed after storage, mark the file rejected and keep it unavailable while a defined cleanup process removes it. If the status callback was lost, poll or reconcile the authoritative upload record instead of starting over.

Google’s Cloud Storage lifecycle documentation describes automated object actions as asynchronous. Lifecycle rules can help clean up abandoned objects, but they cannot be the only mechanism that tells a user whether yesterday’s upload succeeded. Keep explicit upload records and a reconciliation routine for pending states.

Decide how Bubble learns the result

The simplest conceptual approach is for Bubble to request status by upload ID when the user returns to the page. The service reads its authoritative record and responds with a bounded state, display-safe reason, and last-updated time. A callback or event can make the interface feel faster, but it introduces another delivery path that may be duplicated, delayed, or lost. The page should still be able to refresh from the authoritative record.

Bubble’s API Connector can make outbound requests to an integration API. Configure credentials according to Bubble’s API Connector security guidance: do not expose a provider key in page elements, default values, or URL parameters. The service must authenticate the request and recheck that the acting user can read the upload status for its organization. An upload ID is a lookup key, not proof of authorization.

If Bubble records a separate file thing, specify which system owns the status. Two independently editable “available” fields can diverge. One approach is to have the integration service own the state and let Bubble cache a display copy with a version and update time. Another is to make Bubble’s record authoritative after a verified callback. Either can work, but the team must define how it detects and repairs disagreement.

Test the unhappy paths

In a disposable app with synthetic files, run a normal upload, then deliberately interrupt the browser before the first byte, midway through transfer, and after transfer but before final confirmation. Repeat with an expired grant, a duplicate click, a wrong-organization upload ID, an oversized file, and a rejected content type. For each test, inspect the user message, integration record, object state, and audit event. The cross-tenant test plan adds access-control cases.

The most important assertion is that “available” always corresponds to a verified, authorized object, and that a failure does not strand an accessible unvalidated file. The second is that a repeat request has a predictable result. The third is that support can explain an attempt using a correlation ID without reading a secret URL or file contents from the logs.

Measure recovery after launch: how many uploads stay in “checking,” how long they stay there, how often users retry, and how many retries produce duplicate objects. Those metrics can guide improvements, but they need an actual working workflow before any public claim about reliability or speed.

Request early access to discuss your Bubble upload workflow. The first planned Bubble2X workflow focuses on customer-file storage using Google Cloud Storage.

Related reading: upload architecture and file-upload security checklist.

Upload state machine showing selected, uploading, verifying, complete, and failed states

Help shape the cloud layer for Bubble apps

Help shape the cloud layer for Bubble apps

Help shape the cloud layer for Bubble apps

Bubble2X is building a Bubble-to-Google-Cloud integration for controlled customer-file workflows.

Newsletter

Get tips, product updates, and insights about building with Bubble2X.

Subscribe to Bubble2X updates. See our Privacy Notice.

© 2026 Bubble2X. Product in development.

Bubble2X is building a Bubble-to-Google-Cloud integration for controlled customer-file workflows.

Newsletter

Get tips, product updates, and insights about building with Bubble2X.

Subscribe to Bubble2X updates. See our Privacy Notice.

© 2026 Bubble2X. Product in development.

Bubble2X is building a Bubble-to-Google-Cloud integration for controlled customer-file workflows.

Newsletter

Get tips, product updates, and insights about building with Bubble2X.

Subscribe to Bubble2X updates. See our Privacy Notice.

© 2026 Bubble2X. Product in development.