An interrupted migration is not always simply “halfway done.” It may have stopped before saving a unit, after committing some changes, or when the destination returned an ambiguous response. It may also have completed its process and still left discrepancies. To decide how to proceed, the team must reconstruct what happened using verifiable evidence, rather than relying solely on the exit code or the last recorded identifier.
The practical question is not only how to resume a failed data migration in PHP, but also what can be established about the state of each unit and what checks are needed before repeating an operation. This distinction reduces the risk of duplicates, broken relationships, and manual fixes that are difficult to audit.
First, distinguish a stopped run from a migration with discrepancies

A stopped run is one whose process stopped making progress—for example, because of an exception, a lost connection, or operational intervention. A migration with discrepancies, by contrast, is one whose result does not meet the expected conditions, even though the process finished without errors. Rows may have been skipped, keys transformed incorrectly, or relationships left incomplete.
That is why a global status such as completed or failed is insufficient. It is useful to distinguish at least between in progress, stopped, completed, and completed with discrepancies. The global status summarizes the work, but does not replace per-unit records or validation of the result.
Before rerunning the command, preserve the available logs and determine which system is the source of truth at this stage. If there have been writes after the migration, manual changes, or concurrent processes, identify how they affect the comparison. Continuing without this information can turn an investigable discrepancy into a loss of traceability.
Define work units and verifiable checkpoints
Choose a work unit that makes sense for the data and for recovery: it could be an entity, a related group of records, or a stable partition. Avoid making the checkpoint simply “the last ID processed.” At most, that ID shows that the program reached a certain position; it does not prove that all previous records were saved correctly or that the destination is consistent.
For each unit, retain a stable source key, the run version or identifier, its status, and a verifiable result. The checkpoint must be cross-checked against the destination—for example, using a destination key, a fingerprint calculated from relevant fields, or a verification query. If the data can change during processing, also record the selection criteria or cutoff time used.
The design depends on the environment. If the source and destination share a transaction, it can provide evidence of a joint commit; if they are separate systems, do not assume atomicity between them. In an API integration, a lost response can leave it unclear whether the server applied the operation. The strategy must account for this uncertainty and provide a way to check afterward, rather than automatically labeling the unit as failed or confirmed.
Record what was attempted and confirmed for each unit
A useful support record makes it possible to answer who ran the migration, which unit they handled, when its status changed, and what evidence supports that status. Distinguish between processed—the code performed steps—and confirmed—there is a reliable signal that the expected result exists at the destination. Add the attempt number, an error category, and a correlation ID to link process events with external calls.
Statuses should describe operational situations, not just algorithm stages. For example: pending, in progress, confirmation uncertain, confirmed, and requires review. If the process dies while a unit is marked as in progress, treat it as uncertain after restarting until the destination has been checked. Do not mark it as failed without checking whether it produced effects.
Record enough information to investigate, but do not dump credentials, secrets, or unnecessary personal data into logs. Errors should make it possible to identify the unit and operation without allowing traceability to increase data exposure. Also define who can access and retain those records.
Compare invariants before deciding how to proceed
Invariants are conditions that must hold regardless of how the migration is implemented. Counts by type or partition help locate differences, but are not enough: two sets can be the same size and contain different records. Compare business keys, record presence, relationships, and relevant rules, such as permitted statuses or recalculated amounts.
Choose checks that are proportional to the impact and volume. A sample can help with diagnosis, but by itself does not prove that the entire set is correct. For exhaustive validation, compare keys or fingerprints in batches and record which ones were checked. If the transformation deliberately changes fields, document which differences are expected and which indicate an error.
If the source continues to receive changes, separate discrepancies caused by concurrency from those caused by the migration. It may be necessary to set a cutoff window, capture subsequent changes, or repeat a synchronization phase. Without a clear rule for concurrent changes, comparing systems can produce false positives or miss new data.
Resume only with evidence, and handle uncertain results carefully
A safe resume loads the pending and confirmed units, but does not assume that units left in an uncertain state are valid. For these units, query the destination using a stable key and compare the result with the expected transformation. If it matches, record the confirmation without repeating the effect; if it does not exist, retrying may be appropriate. If it exists with different content, stop processing that unit and investigate.
When repeating an operation could create duplicates or external effects—such as sending a notification—use an idempotent operation or an idempotency key if the system supports one. If that is not possible, design a pre-check and a reconciliation path. Do not assume that an exception means nothing was written: the failure may have occurred after the change was committed but before the response was received.
Before resuming, verify that the same mapping and key policy are being used, and that the correct version of the code or configuration is in place. A checkpoint generated by a different transformation can be misleading. In PHP, the migration command should accept an identifiable run and emit structured statuses; this lets someone cross-check the record against the data instead of relying on generic console messages.
Set stop conditions and finish with a report

Stop proceeding when there are key conflicts, invalid data that requires a business rule, orphaned relationships, uncertain results that cannot be verified, or differences that exceed accepted limits. Also stop if the available evidence does not make it possible to determine what happened. In these cases, escalating the issue and preserving the state is usually safer than retrying blindly.
At the end, run the global checks: confirmed units against expected units, counts and keys, relationships, and the business rules defined for the migration. Generate a report stating what was migrated, what was verified, what was retried, and which discrepancies remain unresolved, with references that make them possible to investigate. Do not declare the work complete just because the process exited with code zero.
The criterion for resuming is simple to state, even if it requires technical discipline: each unit must be confirmed, verified as not applied, or set aside for review. This explicit status, together with comparable invariants and useful records, makes it possible to continue a PHP migration without confusing progress with success or uncertainty with authorization to retry.



