A transport fails at 5pm on a release evening and the message on screen is a number. Everything that happens next depends on reading that number correctly, and the most expensive mistakes in SAP change management come from treating all non-zero return codes as the same event.
They are not. Here is what each one means, according to SAP's own documentation, and what to do about it.
The return codes
| Return code | Meaning |
|---|---|
| 0 | Transport ran without errors. |
| 4 | Warnings were issued. All objects were transported successfully. There were special actions for individual objects that may not have been intentional — for example, a warning is issued during the export if the request contains an object deletion. Read the warnings. |
| 8 | Individual objects could not be transported successfully. You must analyze and correct the errors. Examples during import: original object was not overwritten; repaired object was not overwritten. |
| 12 or higher | A critical error has occurred, probably not caused by the contents of the request. Inform your system administrator. |
Two details that are not in that table and matter in practice.
Individual steps produce a limited set of codes. A single transport step can only return 0, 4, 6, 8, 12, 13, 14 or 16. The combined code you see at the end is normally the maximum of the individual step codes.
Codes above 200 are a different animal. If a general error occurs — tp cannot establish a connection to the database, for instance — a single return code above 200 is produced, and the combined return code becomes that general error rather than the step maximum. If you are looking at a three-digit return code, stop investigating the request contents. The problem is the environment.
tp has an explainrc command that prints the short description for a code, and every return code carries a detailed long text in the transport log. Reading it takes ten seconds and is skipped constantly.
Step one: which step failed?
Before anything else, establish where in the import sequence it broke. An import runs as a sequence of steps, and each has a different failure profile:
- Dictionary import — dictionary definitions brought in
- Dictionary activation — definitions activated, possibly with table conversion
- Main import — remaining objects and table entries
- After-import methods / XPRA — follow-up programs
- Generation — ABAP loads regenerated
The transport log in STMS_IMPORT shows a line per step with its own return code. The combined code tells you the severity; the step tells you the cause.
This matters because the same return code means different things at different steps. An 8 at dictionary activation is usually a missing dependency. An 8 at generation is usually a syntax error in a program whose dependency did not travel. An 8 at XPRA is usually a data problem in the target system. Same number, three unrelated investigations.
The common causes, by step
Failures at dictionary activation
A dependency did not travel. The most frequent cause by a wide margin. A structure references a data element, a table references a domain, an append references a base table — and the referenced object is in a different request that has not been imported, or was never captured at all.
*Check:* does the object it depends on exist in the target? If not, find the request containing it (SE03 → Search for Objects in Requests) and establish whether that request is in the queue, sitting unreleased in development, or missing entirely.
*Fix:* import the missing request first. Importing the failing one repeatedly will not help.
Table conversion problems. A field length change or key change on a table that already holds data triggers a conversion. On a large table this is slow, and it can fail on space, on a lock, or on data that does not fit the new definition. This is the failure that turns a maintenance window into an incident.
*Check:* SE14 shows the conversion state of the affected table. A table left in an inconsistent state after a failed conversion needs deliberate handling, not a re-import.
*Fix:* this one deserves care. Establish whether the conversion is incomplete or failed outright, whether the table is currently usable, and how much data is involved, before touching anything.
Failures at main import
Original object not overwritten. The target system holds the original version of an object that the request is trying to change. This is a safety check working correctly, and it usually means the landscape's development ownership is not what someone assumed — often the result of an object being created directly in QA or production at some point in the past.
Repaired object not overwritten. Someone applied a repair to the object in the target system and it has not been confirmed. Overwriting it would discard their fix.
Both of these are the system protecting you. The fix is to understand *why* the target holds a competing version, not to suppress the check.
Failures at XPRA / after-import methods
These run application logic after the objects arrive, and they fail on data rather than on objects. A conversion program expects a configuration entry that is not there; a follow-up routine hits inconsistent master data.
Objects have already been imported at this point. The system now has the new code and an incomplete follow-up — a genuinely partial state, and one that can look fine in a smoke test.
*Check:* the XPRA log names the program. Read what it was doing.
*Fix:* usually the missing prerequisite (often a Customizing request that should have been imported first), followed by re-running the step.
Failures before any step
`tp` cannot reach the database, or the transport directory is unavailable. Return code above 200, or a step that never starts.
*Check:* filesystem availability and permissions on /usr/sap/trans, free space in it, and whether the database is up on the target.
Free space is worth calling out. A full transport directory produces failures across every system in the domain simultaneously, and the error messages do not say "disk full." If several unrelated transports fail at once, check space first.
The things that make it worse
Re-importing with unconditional modes because the import failed. Unconditional modes suppress checks. If the check was correct — an original should not be overwritten, a repair should not be discarded — suppressing it destroys work, silently, with a successful return code. Every unconditional mode should have a reason someone can state out loud.
Importing the rest of the queue to "get past it." A queue is ordered because the order matters. Skipping a failed request and importing the ones behind it produces a target system in a state that no development system was ever in, and the next failure will be harder to diagnose than this one.
Restarting a hung import without checking `TRBAT`. If an import appears stuck, TRBAT shows which step is outstanding and TRJOB links it to a background job. If entries exist but no job is running, the import has died and left state behind. Restarting on top of that state is how one failed transport becomes a landscape problem.
Fixing it directly in the target. Making the change by hand in QA so testing can proceed means the change now exists in QA and in no transport request. It will be absent in production, and the discrepancy will be found at the worst possible moment.
A workable triage order
- Read the combined return code. Above 200 means environment, not content.
- Open the transport log and find the failing step.
- Read the long text for the return code. It is specific.
- For an 8, get the object list of what failed — partial import means the target is in a mixed state.
- Establish whether the cause is a missing dependency, a competing version in the target, or a data problem.
- Fix the cause. Re-import the original request unchanged.
- Only consider unconditional modes when you can articulate what check you are suppressing and why it is safe.
The preventable ones
Most transport failures are not really transport failures. They are process gaps that surface at import time:
- Requests released out of dependency order. If B depends on A, releasing B first guarantees a failure downstream.
- Objects created directly in QA or production. Every one of these becomes an "original object not overwritten" error later.
- Unconfirmed repairs. Repairs are meant to be temporary. Unconfirmed ones accumulate and block future imports.
- No import schedule. Landscapes that import on request rather than on schedule accumulate long queues, and long queues fail in more interesting ways.
- Transport directory unmonitored. Disk space on
/usr/sap/transshould be alerting, not discovered.
Fixing the process removes whole categories of failure. Fixing the individual transport removes one.
If your transport failures have become routine enough to feel normal, that is usually a landscape and process problem rather than bad luck. It is exactly the kind of thing a Basis health check is for, and it is part of what managed Basis operations is meant to prevent.
Related reading
- SAP Transport Management System (STMS): A Practical Guide — how the pipeline is configured
- SAP Transport Tables: E070, E071 and What Each One Holds — querying transport data directly