Skip to main content

Error Handling & Troubleshooting

This guide covers error formats, HTTP status codes, retry strategies, and common troubleshooting scenarios for both the REST and SOAP APIs.

REST Error Format

All REST API errors return a consistent JSON structure regardless of the HTTP status code:

{
"success": false,
"errorCode": "InvalidSession",
"message": "The session token is invalid or has expired.",
"data": null
}
FieldTypeDescription
successbooleanAlways false for error responses
errorCodestringMachine-readable error identifier
messagestringHuman-readable description
dataobject/nullAlways null for error responses

HTTP Status Codes

StatusMeaningCommon Cause
200 OKSuccessRequest processed normally
400 Bad RequestInvalid inputMissing required field, malformed JSON, constraint violation
401 UnauthorizedAuthentication failedMissing, expired, or invalid API key / token
403 ForbiddenInsufficient permissionsUser AuthLevel does not allow this operation
404 Not FoundResource not foundIncorrect ID, resource deleted, or wrong endpoint path
429 Too Many RequestsRate limit exceededToo many calls in a short window — back off and retry
500 Internal Server ErrorServer-side faultUnexpected error — retry with backoff; contact support if persistent

Common REST Error Codes

Error CodeHTTP StatusDescription
InvalidSession401Session token is missing, malformed, or expired
InvalidAPIKey401The X-API-Key value is not recognized
InvalidParameter400A required parameter is missing or its value fails validation
AccessDenied403The authenticated user lacks the required AuthLevel
ObjectNotFound404The requested resource does not exist
DuplicateObject400An object with the same key already exists
RateLimitExceeded429Too many requests — see retry guidance below
InternalError500Unexpected server-side error

See Error Codes for the complete list.

REST C# Exception Handling

// .NET 10 — structured error handling for REST API calls
using System.Net.Http.Json;

record ApiError(bool Success, string? ErrorCode, string? Message);

/// <summary>Executes a REST call and surfaces API errors as exceptions.</summary>
static async Task<T> ExecuteAsync<T>(HttpResponseMessage response)
{
// Deserialize whether success or failure
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<ApiResponse<T>>();
return result!.Data!;
}

// Try to parse the structured error body
ApiError? error = null;
try
{
error = await response.Content.ReadFromJsonAsync<ApiError>();
}
catch { /* fall through to generic message */ }

var code = error?.ErrorCode ?? "Unknown";
var message = error?.Message ?? response.ReasonPhrase ?? "Unknown error";

throw response.StatusCode switch
{
System.Net.HttpStatusCode.Unauthorized => new UnauthorizedAccessException($"[{code}] {message}"),
System.Net.HttpStatusCode.Forbidden => new UnauthorizedAccessException($"[{code}] {message}"),
System.Net.HttpStatusCode.NotFound => new KeyNotFoundException($"[{code}] {message}"),
System.Net.HttpStatusCode.TooManyRequests => new HttpRequestException($"Rate limit exceeded: {message}"),
_ => new HttpRequestException($"[{(int)response.StatusCode}] [{code}] {message}")
};
}

SOAP Error Format

SOAP errors are returned as standard SOAP Faults. ECGridOS extends the fault with a numeric error code.

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Invalid SessionID</faultstring>
<detail>
<ECGridOSSOAPErrorCode>5</ECGridOSSOAPErrorCode>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>

SOAP C# Exception Handling

When using dotnet-svcutil, SOAP faults surface as FaultException:

// .NET 10 — SOAP fault handling with dotnet-svcutil proxy
using System.ServiceModel;

var sessionID = string.Empty;
try
{
sessionID = await client.LoginAsync(email, password);
var parcelList = await client.ParcelInBoxAsync(sessionID, mailboxID, begin, end);
}
catch (FaultException fault)
{
// fault.Message contains the SOAP faultstring
// fault.Code.Name is "Client" or "Server"
Console.Error.WriteLine($"SOAP Fault [{fault.Code.Name}]: {fault.Message}");

// Check ECGridOSSOAPErrorCode in fault.Detail if needed
}
catch (CommunicationException ex)
{
// Network-level error (timeout, connection refused, etc.)
Console.Error.WriteLine($"Communication error: {ex.Message}");
}
finally
{
if (!string.IsNullOrEmpty(sessionID))
await client.LogoutAsync(sessionID);
}

Retry Guidance

Not all errors are permanent. Use exponential backoff for transient failures.

When to Retry

ConditionRetry?Strategy
429 Too Many RequestsYesExponential backoff, honour Retry-After header if present
500 Internal Server ErrorYesExponential backoff, max 3 attempts
503 Service UnavailableYesExponential backoff, max 3 attempts
401 UnauthorizedNoFix credentials — retrying will not help
403 ForbiddenNoFix permissions — retrying will not help
400 Bad RequestNoFix the request payload
404 Not FoundNoVerify the resource ID

C# Retry with Exponential Backoff

// .NET 10 — simple exponential backoff helper
static async Task<HttpResponseMessage> SendWithRetryAsync(
HttpClient http,
Func<HttpRequestMessage> buildRequest,
int maxAttempts = 3,
CancellationToken ct = default)
{
var delay = TimeSpan.FromSeconds(1);

for (var attempt = 1; attempt <= maxAttempts; attempt++)
{
// Build a fresh HttpRequestMessage each attempt — messages cannot be reused
var request = buildRequest();
var response = await http.SendAsync(request, ct);

var isTransient =
(int)response.StatusCode == 429 ||
(int)response.StatusCode >= 500;

if (!isTransient || attempt == maxAttempts)
return response;

// Honour Retry-After if the server provides it
if (response.Headers.RetryAfter?.Delta is { } retryAfter)
delay = retryAfter;

await Task.Delay(delay, ct);
delay *= 2; // double the delay each attempt
}

throw new InvalidOperationException("Retry loop exited without returning a response.");
}

Common Troubleshooting Scenarios

401 Unauthorized on every REST request

Likely cause: The API key is not being sent, or the header name is wrong.

Check: The header must be spelled exactly X-API-Key (case-sensitive on some proxies):

X-API-Key: your-api-key-here

Not x-api-key, APIKey, or Authorization: ApiKey ....


403 Forbidden when calling a management endpoint

Likely cause: The authenticated user's AuthLevel is insufficient for the operation.

Check: Verify the user's AuthLevel using GET /v2/users/me (REST) or WhoAmI() (SOAP). Operations on network-level resources require at least NetworkAdmin.

See AuthLevel values in the Enums appendix.


Inbox list returns empty — no parcels

Check the following in order:

  1. You are querying the correct mailboxID. Use GET /v2/users/me to confirm the default mailbox ID for your session.
  2. Files may already have been downloaded and confirmed. Confirmed parcels no longer appear in the default inbox list.

SOAP session expired mid-process

Cause: ECGridOS sessions expire after a period of inactivity. For long-running batch processes, session expiry can occur between calls.

Fix: Catch FaultException with a session-related fault code and re-authenticate:

// Re-login on session expiry — simplified pattern
catch (FaultException fault) when (fault.Message.Contains("SessionID", StringComparison.OrdinalIgnoreCase))
{
sessionID = await client.LoginAsync(email, password);
// Retry the failed operation
}

For persistent processes, prefer a REST API key which never expires.


dotnet-svcutil proxy throws CommunicationException

Check: Ensure System.ServiceModel.Http NuGet package is installed and the endpoint URL is correct:

https://os.ecgrid.io/v4.1/prod/ECGridOS.asmx

Not http:// — the service requires TLS.