Integration is where elegant Pega applications meet the messy reality of external systems: flaky networks, expired certificates, undocumented payload changes, and timeouts that surface at the worst possible moment. Connector exceptions are rarely about the happy path you tested in the Integration Designer — they are about what your application does when the response never arrives, arrives malformed, or arrives with an HTTP 500. This tutorial walks through how Pega's Connect-REST and Connect-SOAP rules actually report failures, and how to build exception handling that degrades gracefully instead of leaving cases stuck.
How Pega models a connector and its data
When you point the Integration Designer at an OpenAPI/Swagger spec or a WSDL, Pega generates a connected layer for you: a data source, the connector rule (Connect-REST or Connect-SOAP), request and response data transforms, and the page classes that form the generated data model. The connector is the boundary; everything inside Pega works against clay-model clipboard pages, while the connector serializes those pages to JSON or XML on the way out and deserializes the response on the way back.
The pieces you will be debugging are:
- The connector rule — the endpoint URL, HTTP method, authentication profile reference, timeouts, and the request/response mapping.
- The authentication profile — Basic, OAuth 2.0, or a custom scheme. For a deeper treatment of token-based flows see our companion article on OAuth 2.0 and SAML authentication.
- Request and response data transforms — where you map Pega properties to the wire format and back.
- The invoking activity — typically a
Connect-RESTmethod step, followed by status-handling logic.
Understanding which layer failed is the entire game. A ConnectionProblem is a transport failure; an HTTP 422 with a clean body is an application-level rejection. They demand different handling.
Endpoint and authentication configuration
A connector's endpoint is assembled from the resource path plus any path/query parameters mapped in the request transform. Keep environment-specific values (host, base path, credentials) out of the rule itself and in a dynamic system setting or data instance so the same rule promotes cleanly from DEV to PROD.
For authentication, reference an authentication profile rather than hardcoding headers. A basic profile injects an Authorization: Basic header; an OAuth 2.0 profile mints and caches a bearer token. A minimal Connect-REST request payload assembled in a data transform might look like this:
{
"customerId": "CUST-00481",
"requestContext": {
"channel": "Pega",
"correlationId": "a1f3-9920-bc12"
},
"lineItems": [
{ "sku": "VO-PLAN-B", "qty": 1 }
]
}
Always set an explicit Content-Type and Accept on the connector (for REST) or the correct SOAP Content-Type (text/xml for SOAP 1.1, application/soap+xml for SOAP 1.2). A surprising share of "the integration broke" tickets trace back to a server that rejects a charset it did not expect, or a missing Accept header that causes the endpoint to return HTML instead of JSON.
The status contract: pyStatusMessage, pyStatusValue, and method status
Every connector method writes a result to the step page, and to a small set of properties you should treat as the canonical status contract:
| Property / signal | Meaning | Typical use |
|---|---|---|
Method StepStatusGood / StepStatusFail | Did the connector step itself succeed? | Drives when transitions in the activity |
pyStatusMessage | Human-readable status text | Logging, displaying to a user, audit trail |
pyStatusValue | Coarse status (Good / Fail) | Branching logic |
pyHTTPResponseCode (on the response page) | The actual HTTP status code | Mapping 4xx vs 5xx behavior |
ConnectionProblem exception | Transport-level failure (DNS, TLS, timeout, refused) | Triggers retry / circuit-breaker |
The crucial distinction: a Connect-REST step can come back StepStatusFail for two very different reasons. Either the call never completed (a ConnectionProblem — the socket timed out, the host was unreachable, the TLS handshake failed), or the call completed but returned a non-2xx code that you have configured to treat as a failure. You must inspect pyHTTPResponseCode to tell them apart, because retrying a 400 is pointless while retrying a 503 is often correct.
Here is the canonical shape of status handling in an invoking activity. Notice the explicit transition on the step and the branch on the HTTP code:
Step 1: Connect-REST PostOrder StepPage: OrderRequest
; on StepStatusFail -> Jump to Step 4 (handle failure)
Step 2: Property-Set .OrderConfirmed = OrderResponse.confirmed
Step 3: Exit-Activity (success)
Step 4: When OrderRequest.pyHTTPResponseCode >= 500
; true -> Property-Set .RetryEligible = "true" ; Jump to retry
; false -> Property-Set .RetryEligible = "false"
Step 5: Log-Message "Connector failed: "
+ OrderRequest.pyStatusMessage
+ " HTTP=" + OrderRequest.pyHTTPResponseCode
Capture pyStatusMessage and pyHTTPResponseCode into a case-level property or a work-status note so support staff can see why a case is stuck without re-running a Tracer.
Mapping HTTP response codes to behavior
Do not collapse every non-200 into a single "error" path. A robust handler classifies the response code and reacts accordingly:
- 2xx — success; run the response data transform and continue.
- 3xx — usually a redirect or a
Locationyou must follow; rarely expected for an API and worth alerting on. - 400, 422 — your request is wrong (validation, schema). Do not retry. Surface a meaningful error and stop.
- 401, 403 — authentication/authorization. Invalidate any cached token and retry once; if it persists, the credential or scope is wrong.
- 404 — the resource or endpoint is wrong. Treat as a configuration issue, not a transient one.
- 429 — rate limited. Honor
Retry-Afterand back off. - 5xx — server-side; eligible for bounded retry with backoff.
For SOAP, the equivalent of a structured error is the SOAP Fault — the HTTP code may even be 500 while the body carries faultcode and faultstring. Parse the fault in the response transform rather than treating it as an opaque transport failure.
Retry and timeout configuration
Connectors expose connection and read (socket) timeouts. Set them deliberately: a default that is too long ties up a requestor thread while a dead endpoint hangs; too short and you abandon slow-but-healthy calls. As a starting point, a connect timeout of a few seconds and a read timeout aligned to the endpoint's p99 latency is reasonable.
For retries, prefer Pega's built-in invocation with retry semantics or an explicit loop in the activity over an unbounded while. The rules of safe retry:
- Only retry idempotent operations, or operations the endpoint deduplicates via your
correlationId. - Use exponential backoff with jitter — never a tight loop.
- Cap total attempts (3 is a common ceiling) and total elapsed time.
- Retry only on
ConnectionProblem, 5xx, and 429 — never on 4xx.
A bounded backoff loop in an activity reads roughly like this:
// Pseudocode for an activity-driven retry with backoff
int maxAttempts = 3;
long baseDelayMs = 500;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
runConnector("PostOrder"); // Connect-REST step
int code = stepPage.getInt("pyHTTPResponseCode");
boolean transient = (code >= 500) || (code == 429)
|| connectionProblemRaised;
if (!transient) {
break; // success or non-retryable
}
long delay = (long)(baseDelayMs * Math.pow(2, attempt - 1));
long jitter = (long)(Math.random() * 250);
Thread.sleep(delay + jitter); // backoff before next try
}
In production you would not literally Thread.sleep a synchronous requestor for long waits — push the retry into a queue processor or a wait/SLA step so the user is not blocked and threads are not pinned.
Simulating responses and integration testing
You should never need a live endpoint to test your exception paths. Pega's simulation support lets you register a simulated response for a data source so the connector returns a canned payload (including error codes) without leaving the platform. Use it to deterministically exercise:
- A 500 path and confirm your retry kicks in.
- A 422 with a structured error body and confirm you stop and surface it.
- A malformed JSON body and confirm your response transform fails safely rather than corrupting the clipboard.
Pair simulations with unit test cases so a regression in your error handling is caught in the pipeline, not in production.
Logging and Tracer for connector debugging
When a connector misbehaves, the Tracer is your first stop. Enable it, filter to Connector and Services, and run the activity. For each connector invocation Tracer shows the resolved endpoint, the outbound request, the raw response, the HTTP code, and any exception. Reading the actual bytes on the wire instantly distinguishes "we sent the wrong thing" from "they sent us the wrong thing."
Beyond Tracer, raise the log level for the integration packages and watch PegaRULES logs. A typical connector failure entry looks like:
2026-05-23 11:42:07,318 [ TP-Processor3 ] [ STANDARD ] [ OrderAPI:01.01.01 ]
(connector.rest.RESTConnector) ERROR - ConnectionProblem invoking
https://api.partner.example/v2/orders : Read timed out after 30000 ms
2026-05-23 11:42:07,319 [ TP-Processor3 ] [ STANDARD ]
(engine.context) pyStatusMessage=Read timed out pyStatusValue=Fail
Always log your correlationId alongside Pega's data so you can stitch the Pega-side view to the partner's server logs during a joint investigation. For a structured look at the exception classes themselves, our Pega training walks through reading Tracer output line by line.
Resilience patterns: circuit breaker and graceful degradation
A single retry loop is not resilience. When an endpoint is down, hammering it with retries from every requestor makes the outage worse and exhausts your thread pool. Two patterns matter:
- Circuit breaker — track recent failure rate (for example, in a persisted data instance or cache). After a threshold of consecutive failures, "open" the circuit and fast-fail subsequent calls for a cool-down window instead of attempting them. After the window, allow a single probe ("half-open"); on success, close the circuit again.
- Graceful degradation — when the circuit is open or the call ultimately fails, do something useful: serve cached data, queue the request for later processing, route the case to a manual-handling stage, or return a partial result. The user experience should bend, not break.
A practical Pega implementation stores breaker state in a dynamic system setting or data instance, checks it in a when rule before invoking the connector, and updates it from the status-handling steps. Combine that with a queue processor so degraded requests drain automatically once the endpoint recovers.
Common pitfalls
These account for the majority of "it works in DEV, fails in PROD" connector tickets:
- SSL/TLS truststore — the endpoint's certificate (or its issuing CA) is not in Pega's truststore, so the handshake fails with a
ConnectionProblem. Import the full chain into the keystore data instance, not just the leaf cert. Certificate expiry is a recurring outage cause — monitor it. - Content-Type / charset mismatch — sending
application/jsonwhen the server wantsapplication/json; charset=utf-8, or vice versa. SOAP servers are especially strict about the SOAP version's media type. - Large payloads and chunking — very large request or response bodies can exceed limits or time out. Consider pagination, streaming, or
Transfer-Encoding: chunked, and raise read timeouts deliberately for known-large calls. - Proxy and firewall — PROD egress often goes through a proxy DEV does not. A 407 or a connection refused that only happens in PROD is the tell.
- Swallowed faults — treating a SOAP Fault or a structured REST error body as success because you only checked the transport status, not the body.
For a broader catalog of authentication-related failures that masquerade as connector errors, see our deep dive on OAuth 2.0 and SAML configuration.
Key takeaways
- Distinguish transport failures (
ConnectionProblem) from application failures (non-2xx with a body) — they need opposite handling. - Treat
pyStatusMessage,pyStatusValue,pyHTTPResponseCode, and the step status as the status contract, and persist them onto the case for support visibility. - Map HTTP codes to behavior: never retry 4xx; retry 5xx/429/
ConnectionProblemwith bounded exponential backoff and jitter. - Push long retries into queue processors or wait steps so requestor threads are not pinned.
- Use simulations and unit tests to exercise error paths deterministically, and the Tracer plus logs to see the actual bytes on the wire.
- Add circuit breaker and graceful degradation so a downstream outage degrades the experience instead of stalling cases.
- Watch the classic pitfalls: truststore/cert expiry, content-type/charset, large payloads/chunking, and proxies.
Connector debugging is a learnable, repeatable discipline once you internalize the status contract and the retry rules. If you want hands-on coaching through a real integration failure — Tracer in one window, partner logs in the other — our Pega mentorship program pairs you with a senior practitioner. Get in touch and tell us what you are integrating; we will tailor a debugging session to your stack.