strapi transfer dies partway through
strapi transfer runs for a while and then dies, usually with an abort that looks identical no matter what actually failed. The trick is that the failure stage tells you the cause and the error string does not. Read the log for where it died: entities, links or assets. Each points at a different problem and a different fix. And before you plan around it, know that the old folklore is wrong: since 5.52.2 a transfer no longer wipes the destination.
00· Before anything
First: read the log for the stage, not the error
Every stage of a transfer can end in the same abort, so the error string is nearly useless on its own. What matters is the stage the log died in. Transfer moves entities, then links, then assets, and a failure in each is a different article's worth of cause behind one identical-looking message.
Find the last stage that made progress before the abort. Entities, links and assets each point somewhere different below.
Do not retry blindly. A transfer that dies in assets and one that dies in links need opposite fixes, and rerunning the whole thing just burns time and, on large media sets, bandwidth.
Likely causes · The order I check them
Cause 01 of 05
Assets: the WebSocket closes on large media sets
The transfer gets through entities and links, then dies in the assets stage, on projects with a lot of media.
Confirm the log died in the assets stage, and that the run had been going long enough for a large media transfer to stall.
There is a stall detector, transfer.remote.assetIdleTimeoutMs, default 300000ms, that aborts an asset stream when it makes no forward progress. A slow or briefly stalled large-media transfer trips it.
Raise transfer.remote.assetIdleTimeoutMs, and split the run so assets move on their own: --exclude files for the first pass, then --only files for the media.
# everything except media
strapi transfer --to <dest> --exclude files
# then media on its own, with a longer idle timeout
strapi transfer --to <dest> --only filesassetIdleTimeoutMs and its default are documented. Splitting the run with --exclude/--only is the practical workaround for a media set large enough to trip the detector.
Cause 02 of 05
Links: a foreign-key constraint aborts the transaction
The transfer dies in the links stage, and everything after the first failure reports current transaction is aborted.
Confirm the stage is links, and find the first constraint error. The cascade of aborted-transaction messages after it is noise, not the cause.
A foreign-key constraint on a _lnk table aborts the transaction, and every statement after it fails. In at least one case this traced to inconsistent draft/published state on components.
Reconcile the draft and published state before retrying, so the links being transferred all resolve to entries that exist in the same state.
The foreign-key abort is visible in the log; the draft/published component state as the underlying cause is observed, not documented.
Cause 03 of 05
Entities: value too long for the column
The transfer dies early, in the entities stage, with value too long for type character varying(255).
A value overflows a varchar column. The specific instance seen repeatedly is long filenames overflowing files.formats.
Find and shorten or migrate the overflowing values before transferring, or widen the column on the destination to match. Long filenames in files.formats are the usual culprit.
Cause 04 of 05
The reverse proxy isn't forwarding the WebSocket upgrade
The transfer fails almost immediately, or never establishes, against a destination behind a reverse proxy.
Transfer uses a WebSocket. A reverse proxy that does not forward the upgrade header kills the connection before it starts.
Configure the proxy to forward the WebSocket upgrade through to the destination.
The nginx WebSocket requirement is documented on the transfer troubleshooting page.
Cause 05 of 05
Wrong Postgres role on a manual restore
A manual restore fails with permission denied for schema public.
The restore is running as a Postgres role that does not own, or cannot write to, the public schema.
Run the restore as the role that owns the schema, or grant the necessary privileges on public before restoring.
06
The correction worth the whole page
The folklore is that a transfer wipes the destination. That is now wrong, and it is worth correcting loudly. Since 5.52.2 the documented behaviour is replace-and-preserve: transferred stages are replaced, omitted stages are preserved, and admin types and content-releases are always preserved. If you filter with --only or --exclude, the omitted stages survive. So the split-run fix above is safe: excluding files on the first pass does not destroy the files already at the destination.
One conclusion I draw that the docs do not state: there is no supported path from a v4 instance into a fresh v5 instance via transfer. What the docs actually say is that transfer uses strict schema matching, and that v4 to v5 is an in-place upgrade. Those support the conclusion, but they are not the same as stating it, so treat it as an inference and verify against your versions.
Common questions
Not since 5.52.2. The behaviour is replace-and-preserve: transferred stages are replaced, omitted stages are preserved, and admin types and content-releases are always kept. Filtering with --only or --exclude leaves the omitted stages intact.
Raise transfer.remote.assetIdleTimeoutMs, which defaults to 300000ms, and split the run: --exclude files first, then --only files. Large media sets trip the idle-stall detector on a single combined run.
Treat that as unsupported. Transfer uses strict schema matching, and v4 to v5 is designed as an in-place upgrade rather than a transfer. This is an inference from the documented behaviour rather than a documented statement, so verify against your versions before relying on it.
Diagnosed everything and still stuck?
S/01 — Performance & Architecture Rescue.
A fixed-scope week that finds the cause and fixes what's causing it.