Skip to content

Image Storage transfer can remain at 0% when scheduled work does not execute #1108

Description

@pirate-bot

Summary

An Image Storage transfer-back operation can remain active without processing images. The operation is expected to either begin restoring files or enter a visible failure state. Instead, the inspected workflow can continue reporting an active transfer with an unchanged count, leaving the user unable to recover locally stored originals through the plugin.

Customer context

  • Product / area: Optimole Pro, Image Storage transfer back
  • Version: Customer version not provided; inspected source is 4.2.10
  • Environment: WordPress; Windows 10 and Firefox reported by ticket metadata
  • Integration / third party: The customer reports no caching plugin or server cache and reports cron as active
  • Reported error / symptom: Transfer remains stuck before visible progress after password protection was removed
  • Impact: Cloud-stored images cannot be restored to the site's local media storage through the plugin

Reproduction notes

Reported workflow:

  1. Start an Image Storage sync/transfer-back operation.
  2. Remove the site's password gate and retry while images are publicly accessible.
  3. Observe that the transfer remains active before visible progress.

Support later confirmed public image access. The customer reports no cache and active cron. No controlled runtime reproduction was performed; the scheduler-failure condition is confirmed from source, while its occurrence on this site remains inferred.

Diagnosis

Conclusion

The transfer state is persisted as active before background work is scheduled, while the scheduler result is discarded. Polling derives activity from that persisted setting and remaining image count rather than from worker execution or a progress heartbeat. This directly permits a non-progressing operation to remain active indefinitely when the scheduled action is absent, stale, or not executing. The ticket's exact environmental trigger is unknown because no logs or usable screenshot were available.

Where this likely occurs

  • inc/media_offload.php — Optml_Media_Offload::move_images() lines 1854–1934: stores transfer metadata and active status, invokes schedule_action() without evaluating its result, then returns the persisted activity state.
  • inc/media_offload.php — Optml_Media_Offload::schedule_action() and is_scheduled() lines 1946–1970: delegates to Action Scheduler or WP-Cron and treats the presence of a scheduled record as sufficient evidence of recoverable work.
  • inc/media_offload.php — Optml_Media_Offload::maybe_reschedule() lines 197–219: skips recovery whenever a scheduled record exists, without checking whether image counts are advancing.
  • assets/src/dashboard/utils/api.js — callSync() lines 479–590: polls every ten seconds while status is true and count is nonzero, with no no-progress boundary.
  • Git history: commit 52ebcd90 refactored the current scheduling/REST path; the inspected behavior is present in tags from v4.0.0 through v4.2.10.

Engineering notes

The same bulk-processing path handles offloading and rollback, although this report concerns transfer back. Action Scheduler is preferred when available, with WP-Cron as fallback. A pending Action Scheduler record can suppress maybe_reschedule() even when no batch advances. Persistent batch exceptions also retry the same step without a terminal attempt limit in Optml_Media_Offload::start_processing_images() at lines 2035–2101. The customer reports cron as active, but Action Scheduler state, request logs, worker execution, and server resource limits were not captured.

Test coverage status

tests/test-media.php includes test_image_sync() at lines 326–346 and test_image_rollback() at lines 348–358, but these call media operations directly. No relevant coverage was found during inspection for scheduler insertion failure, stale pending actions, non-advancing jobs, REST polling, or terminal handling of persistent batch failures.

What to verify or explore next

  • May be worth reproducing a rollback with scheduler insertion returning failure and confirming the REST count/status sequence.
  • May be worth reproducing a pending Action Scheduler record whose worker does not run, while WP-Cron requests continue normally.
  • If reproducible, checking Action Scheduler records and Optimole rollback logs for the affected operation could distinguish missing dispatch from a repeatedly failing first batch.
  • The direct rollback suites in tests/test-media.php and a status-poll integration path are relevant verification surfaces.

Unknowns / follow-up

The customer's Optimole version, image count, PHP/WordPress versions, Action Scheduler backend, first rollback log error, and exact scheduler state are unavailable. The original screenshot was not retrievable through the sanctioned ticket-image tool.

Confidence

Confidence: 90/100

Two independently verifiable defects in the Optimole WordPress plugin match the reported stalled rollback and disabled Image Storage UI. The screenshot and site-specific logs are unavailable, so confidence is based on direct source-path evidence rather than a customer-site reproduction.


Source: HelpScout #3411333716
Generated by bug-report-triage (ID: bug-report-triage_6a7f1f81ead033.88694331)

Activity

  1. added theissue type on Aug 14, 2026
  2. pirate-bot commented on Sep 22, 2026

    @pirate-bot
    CollaboratorAuthor

    Another customer reported this via support.

    The customer experienced an Image Storage transfer-back operation that remained active for over 20 hours with minimal progress, preventing local image restoration and plugin deactivation. Investigation of version 4.2.14 confirms the workflow permits non-advancing states where the dashboard polls indefinitely on an active status while the worker retries persistent exceptions indefinitely without terminal boundaries or deadlocks. Key code locations include assets/src/dashboard/utils/api.js (callSync()) and inc/media_offload.php (Optml_Media_Offload::start_processing_images() and rollback_and_update_images()).


    Source: HelpScout #3456031569
    Generated by bug-report-triage (ID: bug-report-triage_6ab1fd60523cc3.75850007)

  3. pirate-bot commented on Sep 22, 2026

    @pirate-bot
    CollaboratorAuthor

    Another customer reported this via support.

    A customer reported an Image Storage rollback that remained active for roughly two days without restoring cloud-stored images to the local WordPress Media Library. This matches open issue #1108, where rollback can remain marked as active while background processing makes no progress. The investigation points to Optml_Media_Offload::move_images() and related methods in inc/media_offload.php for persisting transfer state without verifying worker progress, and assets/src/dashboard/utils/api.js for polling active statuses. Exact scheduler logs and site-specific trigger details remain unknown.


    Source: HelpScout #3458633804
    Generated by bug-report-triage (ID: bug-report-triage_6ab26de1374543.30050428)

  4. added
    bugThis label could be used to identify issues that are caused by a defect in the product.
    on Oct 19, 2026
  5. self-assigned this
    on Oct 20, 2026
  6. milicavs commented on Oct 20, 2026

    @milicavs

    🧪 QA steps and test links

    When a step of an Image Storage transfer keeps failing (for example a cloud lookup that errors every time), Optimole retries that same step every 10 seconds forever. The transfer back to the site stays "in progress" for hours or days, and the images never come back. Normal use can't force the failure, so a Code Snippet simulates it.

    Requires: a connected Optimole account on a plan that includes Image Storage, the Code Snippets plugin, and a site where WP-Cron runs (keep the dashboard tab open so requests keep reaching the site).

    Reproduce on Optimole 4.2.17:

    1. Upload 3 images to Media → Add New Media File.
    2. Go to Optimole → Settings → Image Storage, select Optimole Cloud only, click Transfer to Optimole Cloud and wait for "Transfer Complete".
    3. Go to Snippets → Add New, title QA force transfer failure, paste the code below, choose Run snippet everywhere and click Save Changes and Activate.
    4. Back in Image Storage, select Optimole Cloud and your website and click Transfer back from Optimole.
    5. Leave the tab open for 5 minutes, then click Show Logs.
    add_action( 'pre_get_posts', function () {
    	if ( doing_action( 'optml_start_processing_images' ) ) {
    		throw new Exception( 'QA forced transfer failure' );
    	}
    } );
    • Bug: the log repeats "QA forced transfer failure" about every 10 seconds with no end, and the transfer stays in progress with nothing restored.
    • Fixed: after 5 failures the log shows "Step 0 failed 5 times, moving on to the next one.", the same for the next step, then "Process finished" and "... step(s) were skipped after failing 5 times. Start the transfer again to move the images left behind." The transfer ends, and no "Transfer Complete" notice appears.
    Edge cases
    • Deactivate the snippet, then start the transfer back again: images are restored and "Transfer Complete" appears.
    • Deactivate the snippet after 2 or 3 failures, while it is still retrying: that step goes through, nothing is skipped, and "Transfer Complete" appears.
    • Click Cancel the transfer from Optimole mid-retry, then start again: the new transfer gets 5 fresh attempts per step.
    • Snippet active during Transfer to Optimole Cloud (offload): same 5-try limit, and the offload ends without "Transfer Complete".
    • Snippet off, normal transfer back: finishes with "Transfer Complete" exactly as on 4.2.17.

    Assumption: the fix covers a step that keeps failing. A transfer stuck because WP-Cron or Action Scheduler never runs at all is out of scope for this PR.

    Fix: #1194 (open, draft; no build yet)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugThis label could be used to identify issues that are caused by a defect in the product.bug-reportbug-report-triagecustomer reportIndicates the request came from a customer.

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions