All Articles
technical

Decoding CargoWise ProcessingLog Errors: Why HTTP 200 Doesn't Mean Success

CargoWise eAdapter returns HTTP 200 even when your operation fails. Here's how to actually detect errors, parse the ProcessingLog, and stop losing data silently.

Decoding CargoWise ProcessingLog Errors: Why HTTP 200 Doesn't Mean Success

You send a shipment to CargoWise via eAdapter. You get HTTP 200 back. You log "success" and move on. Three days later, operations calls you because the shipment never appeared.

This is the most common eAdapter integration bug, and it happens because CargoWise does not use HTTP status codes the way you expect.

HTTP 200 Means "I Received Your XML"

That is all it means. Not "I processed it." Not "I created the record." Not "your data is valid." Just: "I got the bytes."

The actual result of your operation is buried inside the XML response body, in a structure called UniversalResponse. Whether your shipment was created, partially updated, or completely rejected — the HTTP status is 200 either way.

If your integration only checks the HTTP status code, you have a silent data loss bug. Full stop.

The UniversalResponse Structure

Every eAdapter response follows this shape:

<UniversalResponse xmlns="http://www.cargowise.com/Schemas/Universal/2011/11">
  <Status>ERR</Status>
  <Data>
    <UniversalEvent>
      <Event>
        <EventType>DIM</EventType>
        <EventReference>SHP-00012345</EventReference>
      </Event>
    </UniversalEvent>
  </Data>
  <ProcessingLog>Warning: Consignee 'ACME CORP' not matched - created as local party.
Error: Container number ABCU123456 failed check digit validation.
Error: Unable to save shipment - mandatory field VoyageNumber is empty.</ProcessingLog>
</UniversalResponse>

Three things to notice:

  1. Status can be SUC (success), ERR (error), or PRS (processed with warnings). Always check this first.
  2. Data contains references to created/updated records. On error, this may be empty or contain partial data.
  3. ProcessingLog is where the actual detail lives. And here is the surprise.

ProcessingLog Is Flat Text, Not Structured XML

This catches every developer the first time. You would expect something like:

<!-- What you expect -->
<ProcessingLog>
  <Entry severity="error" code="VAL001">
    <Message>Container number failed validation</Message>
  </Entry>
</ProcessingLog>

What you actually get:

<!-- What you actually get -->
<ProcessingLog>Warning: Consignee 'ACME CORP' not matched - created as local party.
Error: Container number ABCU123456 failed check digit validation.
Error: Unable to save shipment - mandatory field VoyageNumber is empty.</ProcessingLog>

Plain text. Newline-separated. No XML structure inside. No error codes. No severity attributes. Just human-readable strings prefixed with "Warning:" or "Error:".

This means you cannot use an XML parser to extract individual errors. You need string parsing.

How to Parse ProcessingLog Properly

Here is the pattern that works reliably:

function parseProcessingLog(log: string) {
  if (!log?.trim()) return { errors: [], warnings: [], info: [] }

  const lines = log
    .split('\n')
    .map((l) => l.trim())
    .filter(Boolean)

  const errors: string[] = []
  const warnings: string[] = []
  const info: string[] = []

  for (const line of lines) {
    if (line.startsWith('Error:')) {
      errors.push(line.replace('Error:', '').trim())
    } else if (line.startsWith('Warning:')) {
      warnings.push(line.replace('Warning:', '').trim())
    } else {
      info.push(line)
    }
  }

  return { errors, warnings, info }
}

Key rules:

  • Lines starting with Error: are fatal — the operation did not complete
  • Lines starting with Warning: are non-fatal — the operation completed but something was off (e.g., a party was not matched and was created as a local entry)
  • Lines with no prefix are informational — usually confirmation messages

Warnings You Must Not Ignore

Some warnings are functionally errors for your business process:

"Consignee not matched - created as local party" — Your party data did not match an existing organisation in CargoWise. A throwaway local party was created instead. Your operations team will need to manually fix this.

"No matching organisation found for code XYZ" — You sent an org code that does not exist in this CW instance. The party field may be blank on the shipment.

"VGM details not updated - container already sealed" — Your VGM submission arrived after the container status was updated. This can mean missed deadlines and fines.

The Correct Response-Checking Pattern

// 1. Check HTTP status (catches network/auth failures)
if (!response.ok) {
  throw new Error(`eAdapter HTTP error: ${response.status}`)
}

// 2. Parse the XML response body
const xml = await response.text()
const parsed = parseUniversalResponse(xml)

// 3. Check the Status element
if (parsed.status === 'ERR') {
  const { errors } = parseProcessingLog(parsed.processingLog)
  throw new Error(`eAdapter rejected: ${errors.join('; ')}`)
}

// 4. Check for business-critical warnings
if (parsed.status === 'PRS') {
  const { warnings } = parseProcessingLog(parsed.processingLog)
  const criticalWarnings = warnings.filter(
    (w) => w.includes('not matched') || w.includes('not found')
  )
  if (criticalWarnings.length > 0) {
    // Log for operations review, don't necessarily throw
    await flagForReview(parsed.reference, criticalWarnings)
  }
}

The three-layer check — HTTP status, then XML Status, then ProcessingLog content — is the minimum for a reliable integration.

Real-World Error Patterns

After processing thousands of eAdapter responses across production integrations, these are the most frequent errors we see:

Error Message PatternUsual Cause
"Container number failed check digit"ISO 6346 validation — check digit algorithm wrong
"Unable to save - mandatory field empty"Required CW field not mapped in your XML
"No matching shipment found"Wrong DataTarget type (consol vs shipment)
"Duplicate key violation"Sending a create when the record already exists
"Organisation code not found"Org code mismatch between systems

Go Beyond Error Handling

Understanding ProcessingLog parsing is essential, but it is one piece of a larger puzzle. Knowing which errors are recoverable, how to implement retry logic that does not create duplicates, and how to build monitoring dashboards that catch silent failures before operations does — that requires deeper knowledge.

Our Complete eAdapter Integration Guide covers the full error-handling architecture, including retry strategies, idempotency patterns, and a ProcessingLog monitoring template you can deploy immediately.

Get the Complete Guide →

Ready to automate your document processing?

Join freight forwarders saving hours every week with CargoMode.

Start for free