Send Docs Feedback

Fault handling

Many error conditions can arise while API proxies are servicing requests from apps. For example, API proxies might encounter network issues when communicating with backend services, apps might present expired credentials, request messages might be incorrectly formatted, and so on.

When an error occurs after a client app calls an API proxy, an error message gets returned to the client. By default, the client receives an often cryptic error message with no details or guidance. But if you want to replace default error messages with more useful custom messages, and even enrich them with things like additional HTTP headers, you need to set up custom fault handling in Edge.

Custom fault handling also lets you add functionality such as message logging whenever an error occurs.

Fault handling is a major architectural design task for API proxy development. It's important to take the time to figure out how and when you're going to handle errors, determine what error messages will say, and design error message formats. After (or as) you figure those things out, then use this topic to help you implement your own custom fault handling.

Before we talk about implementing custom error handling in your API proxies, it's helpful to understand how errors occur and how API proxies react to them.

How errors occur

First we'll simply cover how errors occur. Knowing how errors occur helps you plan for the different situations in which you want to implement custom error handling.

Automatic errors

An API proxy throws an error automatically in the following situations:

  • A policy throws an error. For example, if an API call sends an expired key, the Verify API Key policy automatically throws an error; or if the number of API calls exceeds a certain limit, the Quota or Spike Arrest policy throws an error. (See the Error code reference for the types of errors policies can throw).
  • There's an issue in the API proxy message flow, such as a routing error.
  • There's a backend failure, such as an HTTP error due to protocol level failures, TLS/SSL errors, or an unavailable target service.
  • There's a system-level failure, such as an out-of-memory exception.

For more information on these errors, see Fault taxonomy in this topic.

Custom errors

For situations where there's not an automatic error, you may want to throw a custom error; for example, if a response contains the word "unavailable", or if the HTTP status code is greater than 201. Do this by adding a Raise Fault policy to the appropriate place in an API proxy flow.

You can add a Raise Fault policy to an API proxy flow the same as you do any other policy. In the following proxy configuration example, the Raise-Fault-1 policy is attached to the TargetEndpoint response. If the word "unavailable" is present in the response from the target service, the Raise Fault policy gets executed and throws an error.

<TargetEndpoint name="default">
...
  <Response>
    <Step>
      <Name>Raise-Fault-1</Name>
      <Condition>(message.content Like "*unavailable*")</Condition>
    </Step>
  </Response>

What API proxies do when errors occur

Here's what happens when a proxy throws an error.

Exit the proxy pipeline

When an API proxy encounters an error, regardless of how it occurs, it exits the normal flow pipeline, enters an error flow, and returns an error message to the client app. Once the API proxy enters the error flow, it cannot return processing back to the normal flow pipeline.

For example, assume an API proxy has policies in the following order in the ProxyEndpoint request:

  1. API Key Verification
  2. Quota
  3. JSON to XML

If an error occurs during API key verification, the API proxy moves into an error flow. The Quota and JSON to XML policies are not executed, the proxy doesn't proceed to the TargetEndpoint, and an error message is returned to the client app.

Check for FaultRules

In the error flow, API proxies also check for the presence of the following (in order) in the API proxy configuration before returning a default error message to the client app:

  1. A <FaultRules> section, which contains the logic to trigger custom error messages (and other policies) based on specific conditions that you define.
  2. A <DefaultFaultRule> section, which triggers a default error message if no <FaultRules> are defined or no existing <FaultRules> get executed.

In essence, the API proxy is giving you the opportunity to return a custom error message and trigger other logic. If the proxy finds neither of those sections, or they exist but no custom fault was triggered, the proxy sends its own Edge-generated default message.

Simple fault handling example

Let's start with a simple example, where a call to an API proxy doesn't contain a required API key. By default, following is the response that gets returned to the client app:

HTTP/1.1 401 Unauthorized
Date: Wed, 20 Jul 2016 19:19:32 GMT
Content-Type: application/json
Content-Length: 150
Connection: keep-alive
Server: Apigee Router

* Connection #0 to host myorg-test.apigee.net left intact
{"fault":{"faultstring":"Failed to resolve API Key variable request.queryparam.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}

Your API users may be able to figure out the error message, but they may not. And many default errors are more subtle and harder to decipher.

As an API developer, it's up to you to change this message to meet the needs of whoever will ultimately receive the error message, whether it's an iOS app developer or an internal testing group that has its own error message format requirements.

Here's a basic example of how you'd create a custom error message to handle this error. This requires 1) a policy that defines the custom message, and 2) a FaultRule that executes the policy when the proxy goes into an error flow.

1. Create a policy that defines the custom message

First, create a policy that defines the custom error message. You can use any type of policy, such as Assign Message, that can set a payload and optional HTTP headers such as status code and reason phrase. Assign Message is ideal for this. It lets you control message payload, set a different HTTP status code, set a different HTTP reason phrase, and add HTTP headers.

Don't attach the policy to any flow in the API proxy. It's enough that it simply exists in the proxy bundle. To do this in the management UI proxy editor, go to the Develop tab, and in the Navigation pane and click the + icon on the Policies bar.

This lets you create a policy without attaching it to a flow in the API proxy.

Following is an example Assign Message policy that:

  • Returns a JSON message.
  • Sets an HTTP status code (911, which is an obvious non-existent status code to simply illustrate the flexibility you have). The status code appears in the HTTP header.
  • Sets an HTTP reason phrase (to replace the default "Unauthorized" reason phrase for this missing API key error). The reason phrase appears next to the status code in the HTTP header.
  • Creates and populates a new HTTP header called invalidKey.
<AssignMessage async="false" continueOnError="false" enabled="true" name="invalid-key-message">
    <DisplayName>Invalid key message</DisplayName>
    <Set>
        <Payload contentType="application/json">{"Citizen","Where's your API key? I don't see it as a query parameter"}</Payload>
        <StatusCode>911</StatusCode>
        <ReasonPhrase>Rejected by API Key Emergency Services</ReasonPhrase>
    </Set>
    <Add>
        <Headers>
            <Header name="invalidKey">Invalid API key! Call the cops!</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

When this policy is executed, the response to the client app will look like the following. Compare it to the default response shown earlier.

HTTP/1.1 911 Rejected by API Key Emergency Services
Date: Wed, 20 Jul 2016 18:42:36 GMT
Content-Type: application/json
Content-Length: 35
Connection: keep-alive
invalidKey: Invalid API key! Call the cops!
Server: Apigee Router

* Connection #0 to host myorg-test.apigee.net left intact
{"Citizen","Where's your API key? I don't see it as a query parameter."}

Yes, it's a little silly, but it shows you what's possible. At least now the developer receiving the message knows he forgot to include an API key as a query parameter.

But how does this policy get executed? The next section shows you.

2. Create the <FaultRule> that will trigger the policy

In the <ProxyEndpoint> or <TargetEndpoint> sections of the proxy configuration, you'll add a <FaultRules> XML block that contains one or more individual <FaultRule> sections. Each FaultRule represents a different error you want to handle. In this simple example, we'll use only one FaultRule to show you what it's composed of.

You should also add a <DefaultFaultRule> to provide a custom general error message if none of your FaultRules is executed.

Example

<ProxyEndpoint name="default">
...
    <FaultRules>
       <FaultRule name="invalid_key_rule">
            <Step>
                <Name>invalid-key-message</Name>
            </Step>
            <Condition>(fault.name = "FailedToResolveAPIKey")</Condition>
        </FaultRule>
    </FaultRules>
    <DefaultFaultRule name="default-fault">
        <Step>
            <Name>Default-message</Name>
        </Step>
    </DefaultFaultRule>

Key points:

  • The FaultRules are defined in the ProxyEndpoint. This is important. More on putting FaultRules in the ProxyEndpoint vs. TargetEndpoint later.
  • <name> - The name of the policy to execute. The name comes from the policy's name attribute on the parent element, as shown in the policy example earlier.
  • <Condition> - Edge evaluates the condition and executes the policy only if the condition is true. (If there are multiple FaultRules that evaluate to true, Edge executes the last one that is true. (More on that in the Multiple FaultRules and execution logic section.) If you don't include a condition, the FaultRule is automatically true. But that's not a best practice. Each FaultRule should have its own condition.

    As you'll see later, Steps can have their own "inner" conditions that determine whether or not a policy gets executed. (More on that in the Multiple FaultRules and execution logic section.) But Step conditions have nothing to do with whether or not a FaultRule gets executed. Edge looks at only the "outer" FaultRule conditions to determine which FaultRule gets executed.

  • <DefaultFaultRule> - If no custom FaultRule is executed, the <DefaultFaultRule> is executed, sending a more generic custom message instead of the cryptic default Edge-generated message. A <DefaultFaultRule> can also have a <Condition>, but in most cases you won't include one, because you want it to execute no matter what as a last resort.

    The DefaultFaultRule is typically used to return a generic error message for any unexpected error. An example would be a message that contains contact information for technical support. This default response serves the dual purpose of providing developer-friendly information while also obfuscating backend URLs or other information that might be used to compromise the system.

Multiple FaultRules and execution logic

In the Custom fault handling example section, we used a simple example of a single FaultRule and condition. In a real-world API project, with all the possible errors that can occur, you're likely to have multiple FaultRules and a DefaultFaultRule in both your <ProxyEndpoint> and <TargetEndpoint>. Ultimately, though, only one FaultRule is executed when an API proxy goes into an error flow.

This section describes the logic Edge uses in handling FaultRules, from how it arrives at a single FaultRule to execute to how "inner" Step conditions are handled when their FaultRule is triggered. This section also provides guidance on when to define FaultRules in the <ProxyEndpoint> vs. the <TargetEndpoint>, and describes the relationship between FaultRules and the Raise Fault policy.

Fault rule execution

"Outer" and "inner" conditions

In this topic we refer to "outer" and "inner conditions. You can also think of them as "FaultRule" and "Step" conditions. The difference is important. Following is an example of a FaultRule that has an outer FaultRule condition and an inner step condition. We'll discuss the differences in this section.

<FaultRule name="over_quota">
            <Step>
                <Name>developer-over-quota-fault</Name>
                <!-- Inner (Step) condition. Only gets executed if 
                     the FaultRule is executed. -->
                <Condition>(ratelimit.developer-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <!-- Outer (FaultRule) condition. Edge evaluates this 
                 to determine whether or not to execute 
                 the FaultRule. -->
            <Condition>(fault.name = "QuotaViolation")</Condition>
        </FaultRule>

In brief, here's the logic Edge uses when an API proxy goes into an error flow:

  1. Reads each FaultRule, top to bottom in the XML block, and evaluates the <Condition> of each <FaultRule> (the "outer" condition, not the "inner" <Step> conditions).
  2. Executes the last FaultRule whose condition is true. If a FaultRule has no condition, it's true by default.
    • When a FaultRule is executed, all Steps inside the FaultRule are evaluated in order, top to bottom in the XML configuration. Steps without conditions are automatically executed (the policies are executed), and Steps that have a <Condition> that evaluates to "true" are executed (conditions that evaluate to "false" are not executed).
    • If a FaultRule is executed, but no Steps in the FaultRule are executed (because their conditions evaluate to "false"), the Edge-generated default error message is returned to the client app. The <DefaultFaultRule> is not executed, because Edge has already executed its one FaultRule.

      The exception to this is if the <DefaultFaultRule> has the child element <AlwaysEnforce>true</AlwaysEnforce>, which executes the DefaultFaultRule even if a FaultRule was executed.

      However, there's one instance where this isn't the case. If a FaultRule other than the DefaultFaultRule invokes a Raise Fault policy, the DefaultFaultRule does not execute, even if the <AlwaysEnforce> element in the <DefaultFaultRule> tag is true.

  3. If no FaultRule is executed, Edge executes the <DefaultFaultRule>, if present.

Let's see an example with inline comments:

<ProxyEndpoint name="default">
...
    <FaultRules>
<!-- This FaultRule is automatically TRUE, because there's no "outer" condition. 
     With multiple FaultRules, it's not a best practice to have a
     FaultRule without an outer condition. -->
        <FaultRule name="random-error-message">
            <Step>
                <Name>Random-fault</Name>
            </Step>
        </FaultRule>
<!-- Let's say this FaultRule is FALSE. 
     A policy did not throw a FailedToResolveAPIKey error. -->
        <FaultRule name="invalid_key_rule">
            <Step>
                <Name>invalid-key-message</Name>
            </Step>
            <Condition>(fault.name = "FailedToResolveAPIKey")</Condition>
        </FaultRule>
    </FaultRules>
<!-- Let's say this fault is TRUE. The Quota policy threw a QuotaViolation 
     error. This is the last FaultRule to be TRUE, so it's executed. 
     Now the Steps are evaluated, and for the ones whose conditions
     evaluate to TRUE, their policies are executed. Steps without
     conditions are automatically true. -->
<FaultRule name="over_quota">
            <Step>
                <Name>developer-over-quota-fault</Name>
                <Condition>(ratelimit.developer-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <Step>
                <Name>global-over-quota-fault</Name>
                <Condition>(ratelimit.global-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <Step>
                <Name>log-error-message</Name>
            </Step>
            <Condition>(fault.name = "QuotaViolation")</Condition>
        </FaultRule>
<!-- If no <FaultRule> is executed, the <DefaultFaultRule> is executed. 
     If a FaultRule is executed, but none of its Steps are executed,
     The DefaultFaultRule is not executed (because Edge has already
     executed its one FaultRule). -->
    <DefaultFaultRule name="default-fault">
        <Step>
            <Name>Default-message</Name>
        </Step>
    </DefaultFaultRule>

Fault rule order

As you can see in the previous example, the order in which you put your FaultRules is important, because the last FaultRule (physically lowest in the XML block) that evaluates to true is executed.

For example, in the following sequence, FaultRule 5 gets executed:

  1. FaultRule 1: FALSE
  2. FaultRule 2: TRUE
  3. FaultRule 3: FALSE
  4. FaultRule 4: TRUE
  5. FaultRule: 5 TRUE

Policies to include

You can execute any policies from a FaultRule by putting them in Steps. For example, you can execute an Assign Message policy to format a response to the client app, then log a message with the Message Logging policy. Policies are executed in the order you put them (top to bottom in the XML).

Fault rules are triggered ONLY in an error flow (about continueOnError)

The heading may seem like we're repeating ourselves, but there's one particular nuance to be aware of with regard to a proxy error causing an API proxy to enter an error flow—or rather, not entering an error flow: the continueOnError attribute on a policy.

To recap: An API proxy evaluates <FaultRules> and <DefaultFaultRule> only if the proxy has entered an error flow. That means that even if a FaultRule condition evaluates to true, it won't get triggered if the proxy isn't in an error flow.

However, here's an example of an error occurring and the proxy not entering an error flow. On any policy, you can set an attribute on the parent element called continueOnError. That attribute is very important with regard to fault handling, because it determines whether or not the proxy enters an error flow if the policy fails. In most cases, you'll want to keep the default continueOnError="false", which puts the proxy in an error flow if the policy fails, and your custom error handling will get triggered. However, if continueOnError="true" (for example, if you don't want the failure of a Service Callout to stop the proxy execution), the proxy will not go into an error flow if that policy fails, and the proxy won't look at your FaultRules.

For information on logging errors when continueOnError="true", see Handling policy faults within the current flow.

Where to define FaultRules: ProxyEndpoint or TargetEndpoint

When an API proxy experiences an error, the error occurs either in the <ProxyEndpoint> (request from or response to client app) or in the <TargetEndpoint> (request to or response from target service). Wherever that error occurs is where Edge looks for FaultRules.

For example, if a target server isn't available (HTTP status code 503), the API proxy would go into an error flow in the <TargetEndpoint> response, and the normal API proxy flow wouldn't continue to the <ProxyEndpoint>. If you have FaultRules defined only in the <ProxyEndpoint>, they won't handle that error.

Here's another example. If a Raise Fault policy on the <ProxyEndpoint> response triggers an error, a FaultRule in the <TargetEndpoint> won't get executed.

FaultRules vs. the Raise Fault policy

Fault rules and the Raise Fault policy may on the surface sound like alternative ways to accomplish fault handling; and in some ways that's true. But they also work together. This section explains the relationship between the two. Understanding this relationship should help you design your fault handling, especially if you want to use both.

In brief:

  • Fault rules are always evaluated when an API proxy enters an error flow.
  • The Raise Fault policy is a way of putting an API proxy in an error flow when an error wouldn't have otherwise occurred.

    For example, if you want to throw an error if the HTTP status code in the response from the target service is greater than 200, you add a Raise Fault policy in your response flow. It would look something like this:

    <ProxyEndpoint name="default">
        <PreFlow name="PreFlow">
    ...
            <Response>
                <Step>
                    <Name>Raise-Fault-1</Name>
    <!-- If the condition is true, the Raise-Fault-1 policy gets executed -->
                    <Condition>(response.status.code GreaterThan "200")</Condition>
                </Step>
            </Response> 

    The Raise Fault policy also sends an error message to the client app.

What happens when a Raise Fault policy triggers an error, which puts the proxy in an error flow, which potentially executes a FaultRule? Here's where things can get a little tricky. If the Raise Fault policy returns an error message and a FaultRule gets triggered and returns an error message, what gets returned to the client app?

  • Since the FaultRule or DefaultFaultRule is executed after the Raise Fault policy, the FaultRule response data wins.
  • The Raise Fault policy response data (status code, reason phrase, or message payload) is used if that data is not set by the FaultRule or DefaultFaultRule.
  • If both the Raise Fault policy and FaultRule add custom HTTP headers, both are included in the response. Duplicate header names create a header with multiple values.

Here's an example of what's set by a Raise Fault policy and a FaultRule, and what gets returned to the client app. The samples are designed for brevity, not for best practices.

Client app receives:

Status Code: 468
Reason Phrase: Something happened
Payload: {"Whoa":"Sorry."}
Header: 
  errorNote: woops,gremlins

<- Fault rules policy sets this:

Status Code: [none] 
Reason Phrase: Something happened
Payload: {"Whoa":"Sorry."}
Header: 
  errorNote: gremlins

<- Raise Fault policy sets this:

Status Code: 468
Reason Phrase: Can't do that
Payload: {"DOH!":"Try again."}
Header: 
  errorNote: woops

If the <DefaultFaultRule> has the child element <AlwaysEnforce>true</AlwaysEnforce>, the DefaultFaultRule is always executed, even if another FaultRule was executed as well. Any payload and/or headers set by a policy in the DefaultFaultRule get returned to the client, since it's the last to execute.

Building conditions

Conditions are the key to FaultRule execution. You create FaultRule conditions the same way you do for other conditions in Edge, such as for conditional flows or Raise Fault conditions.

To put the rest of this section in context, here's a sample fault rule that has an outer FaultRule condition and an inner Step condition.

<FaultRule name="invalid_key_rule">
    <Step>
        <Name>invalid-key-message</Name>
        <Condition>(oauthV2.Verify-API-Key-1.failed = true)</Condition>
    </Step>
    <Condition>(fault.name = "FailedToResolveAPIKey")</Condition>
</FaultRule>

Conditions specific to policy errors

When a policy fails, catch the error in a condition using the fault.name variable. For example:

<Condition>(fault.name = "policy_error_name")</Condition>

The error name appears in the default error message. For example, in the following, the fault name is FailedToResolveAPIKey. In this case, a flow variable called fault.name is set to the value FailedToResovleAPIKey.

{"fault":{"faultstring":"Failed to resolve API Key variable request.queryparam.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}

So the condition would look like this:

<Condition>(fault.name = "FailedToResolveAPIKey")</Condition>

See the Error code reference for a list of policy errors.

Available variables

When an API proxy goes into an error flow, the only available variables for use in conditions are:

  • The variables of the policy that failed.
  • The HTTP message variables that exist at the point of failure. For example, if an error is thrown in the response, a FaultRule in the <TargetEndpoint> could use HTTP data response.status.code, message.content, error.content, and so on. Or if a Quota policy failed, you could use the variable ratelimit.quota_policy_name.exceed.count. Use the Trace tool and the policy reference topics to help you figure out which variables and HTTP data are available.

More information

Best practices for fault handling

Fault handling is a major architectural design task for API proxy development. It's important to take the time to figure out how and when you're going to handle errors, determine what error messages will say, and design error message formats. After (or as) you figure those things out, then use these best practices to help you with your fault handling implementation.

Following are some best practices in designing and building fault handling:

  • For each FaultRule, provide an "outer" <Condition> (sibling to the <Step> element). Fault rules with no outer condition automatically evaluate to true. "Inner" Step conditions are not used to determine whether a FaultRule is true or false. Step conditions are evaluated only after Edge executes the FaultRule containing them. In a FaultRule, it's typical to have multiple Steps with Assign Message (or other) policies, each with a Step condition.
  • Create one FaultRule per policy error you're likely to receive. For example, if you're using the Verify API Key policy, create a FaultRule for each likely error, such as FailedToResolveAPIKey, InvalidApiKey, ApiKeyNotApproved. (See the Error code reference for policy errors.) As you discover additional errors that need to be handled, you can go back later and add them to your FaultRules. It's okay to be iterative, though it does require proxy redeployment.
  • Use inner Step conditions only if you need more fine-grained error control. For example, if you're enforcing both individual developer quota and global quota with two policies in your request flow, set your "outer" FaultRule condition to get triggered on the QuotaViolation error (which is thrown when quota goes over in either case). Then set Step conditions to evaluate the "exceed.count" variables in both of your quota policies. Only the relevant error is sent to the client (developer quota overage or global quota overage).
  • Add FaultRules where the errors will occur (client side <ProxyEndpoint> or target side <TargetEndpoint>), including errors for each type of proxy that exists in each place.
  • In FaultRules, you can execute any type of policy that can return a message to the client app. Assign Message is ideal for this.
  • When using Raise Fault policies in conjunction with FaultRules, coordinate the response data that's sent back when both the Raise Fault policy and a FaultRule return data. For example, if your Raise Fault policy resets the HTTP status code, don't have a FaultRule reset the status code. The worst that can happen is that the default status code gets returned to the client app.
  • <DefaultFaultRule> execution:
    • If you want a <DefaultFaultRule> to always execute when no other FaultRule executes, don't include a <Condition> on it.
    • If you want a <DefaultFaultRule> to always execute even when another FaultRule has executed, add the <AlwaysEnforce>true</AlwaysEnforce> child element.

Creating FaultRules

To add a FaultRule you need to edit the XML configuration of the ProxyEndpoint or TargetEndpoint. You can use the Edge UI to make this edit in the Code pane of the Develop view for an API proxy, or edit the XML file that defines the ProxyEndpoint or TargetEndpoint.

If you create FaultRules in the management UI, first create the policies you want to execute, then add them to the FaultRule configuration. (You'll get an error in the UI if you try to save a FaultRule that references a policy that hasn't been created yet.)

Adding policies to a FaultRule

While you can put any policy in the FaultRule, you commonly use the Assign Message policy. Use the Assign Message policy to generates a custom response message for an error condition. Assign Message enables you to configure an HTTP response with payload, HTTP status code, headers, and reason phrase elements.

A policy used in a FaultRule is typically not used in the normal flow pipeline. Therefore, when using the Edge UI to create a policy for a FaultRule, select the plus sign, "+", next to "Policies" in the UI to add the policy:

The example below shows a typical Assign Message policy configuration:

<AssignMessage name="fault_invalidkey">
  <Set>
      <Payload contentType="text/plain">Contact support at support@mycompany.com.</Payload>
      <StatusCode>401</StatusCode>
      <ReasonPhrase>Unauthorized</ReasonPhrase>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

You can now use this policy in your FaultRule. Notice how you reference the Assign Message policy by name in the FaultRule:

<ProxyEndpoint name="default">
  ...
  <FaultRules>
    <FaultRule name="invalid_key_rule">
      <Step>
        <Name>fault_invalidkey</Name>
      </Step>
      <Condition>(fault.name = "InvalidApiKey")</Condition>
    </FaultRule>
  </FaultRules>
</ProxyEndpoint>

When you deploy the configuration above, the API proxy will execute the Assign Message policy called fault_invalidkey whenever an app presents an invalid API key.

You can execute multiple policies in a FaultRule, as the following example shows:

<ProxyEndpoint name="default">
  ...
  <FaultRules>
    <FaultRule name="invalid_key_rule">
      <Step>
        <Name>policy1</Name>
      </Step>
      <Step>
        <Name>policy2</Name>
      </Step>
      <Step>
        <Name>policy3</Name>
      </Step>
      <Condition>(fault.name = "InvalidApiKey")</Condition>
    </FaultRule>
  </FaultRules>
</ProxyEndpoint>

The policies execute in the order defined. For example, you can use the Message Logging policy, the Extract Variables policy, the Assign Message policy, or any other policy in the FaultRule. Note that processing of the FaultRule stops immediately if either of these situations occur:

  • Any policy in the FaultRule causes an error
  • Any of the policies in the FaultRule is of type Raise Fault

Defining the custom error message returned from a FaultRule

As a best practice, you should define a rules that determine the error responses from your APIs. In that way, you deliver consistent and helpful information your clients. For more on defining consistent error messages, see RESTful API Design: what about errors?.

The example Assign Message policy below uses the <Payload>, <StatusCode>, and <ReasonPhase> tags to define the custom error response sent back to the client:

<AssignMessage name="fault_invalidkey">
  <Set>
    <Payload contentType="text/plain">You have attempted to access a resource without the correct authorization. 
       Contact support at support@mycompany.com.</Payload>
    <StatusCode>401</StatusCode>
    <ReasonPhrase>Unauthorized</ReasonPhrase>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

This response includes:

  • The payload containing the error message and an email address for contacting support.
  • The HTTP status code returned in the response.
  • The reason phrase, which is a short description of the error.

Creating a DefaultFaultRule

A DefaultFaultRule acts an exception handler for any error that is not explicitly handled by another FaultRule. If the conditions for all FaultRules do not match the error, then the DefaultFaultRule handles the error. Default fault handling can be enabled by adding the <DefaultFaultRule> tag as a child element of a ProxyEndpoint or a TargetEndpoint.

For example, the TargetEndpoint configuration below defines a DefaultFaultRule that invokes a policy named ReturnGenericError:

<TargetEndpoint name="default">
  ...
  <FaultRules>
    ...
  </FaultRules>

  <DefaultFaultRule name="fault-rule">
    <Step>
      <Name>ReturnGenericError</Name>
    </Step>
  </DefaultFaultRule>

  <HTTPTargetConnection>
    <URL>http://mocktarget.apigee.net</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

The DefaultFaultRule is typically used to return a generic error message for any unexpected error, such as a message that contains contact information for technical support. This default response serves the dual purpose of providing developer-friendly information while also obfuscating backend URLs or other information that might be used to compromise the system.

For example, you define the following AssignMessage policy to return a generic error:

<AssignMessage name="ReturnGenericError">
  <Set>
    <Payload type="text/plain">SERVICE UNAVAILABLE. PLEASE CONTACT SUPPORT: support@company.com.</Payload>
  </Set>
</AssignMessage>

Include the <AlwaysEnforce> element in the <DefaultFaultRule> tag to execute the DefaultFaultRule for every error, even if another FaultRule has already been executed. The DefaultFaultRule is always the last FaultRule to execute:

  <DefaultFaultRule name="fault-rule">
    <Step>
      <Name>ReturnGenericError</Name>
    </Step>
    <AlwaysEnforce>true</AlwaysEnforce>
  </DefaultFaultRule>

One use of the DefaultFaultRule is to determine the type of error that occurs when you otherwise cannot determine it. For example, your API proxy is failing for an error that you cannot determine. Use the DefaultFaultRule to invoke the following AssignMessage policy. This policy writes the fault.name value to a header named DefaultFaultHeader in the response:

<AssignMessage async="false" continueOnError="false" enabled="true" name="DefaultFaultRule">
  <DisplayName>DefaultFaultRule</DisplayName>
  <Set>
    <Headers>
      <Header name="DefaultFaultHeader">{fault.name}</Header>
    </Headers>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
  <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

You can then view the header in the Edge trace tool or on the response to see what caused the error.

Using PostClientFlow with fault handling

For all errors other than policy errors, and for policy errors where the continueOnError element is false, an API proxy immediately enters the error flow. Once the API proxy enters the error flow, it cannot return processing back to the normal flow pipeline.

Edge supports the PostClientFlow in the response flow, which executes after the response is returned to the requesting client app. Only the Message Logging policy can be attached to this flow, which is executed after the response is sent.

It's important to note that the response flow variable is not available in the PostClientFlow once processing enters the error flow. Instead, you can use the message variable to set values in the MessageLogging policy in the PostClientFlow, ensuring that they will be set whether or not the error flow was the previous context. For more information and examples, see "Uses for message variables".  

The PostClientFlow is special in two ways:

  1. It only executed as part of the response flow.
  2. It is the only flow executed after the proxy enters the Error flow.

Because it is executed regardless of whether the proxy succeeded or failed, you can put Message Logging policies in the PostClientFlow and be guaranteed that they always execute.

Note: When the API proxy is in the error flow, the response message variables in Edge are not available in the PostClientFlow. If you want to log response information from the PostClientFlow, use the message object. You can use this object to get at headers and other information from the response whether or not there was an error. See Message variables for more information and an example. 

The following Trace image shows a Message Logging policy executing as part of the PostClientFlow, after the DefaultFaultRule executes:

In this example, the VerifyAPIKey policy caused the fault because of an invalid key. 

Shown below is the ProxyEndpoint definition that includes the PostClientFlow:

<ProxyEndpoint name="default">
  ...
  <PostClientFlow>
    <Response>
      <Step>
        <Name>Message-Logging-1</Name>
      </Step>
    </Response>
  </PostClientFlow>
  ...
</ProxyEndpoint>

Handling policy faults within the current flow

The examples shown so far all use a FaultRule on the ProxyEndpoint or TargetEndpoint to handle any policy errors as part of the error flow. That is because the default value of the continueOnError element of a policy is false, meaning that when an error occurs in a policy, control is directed to the error flow. Once in the error flow, you cannot return control back to the normal pipeline and you typically return some form of error message to the calling app.

However, if you set the continueOnError element to true for a policy, control stays in the current flow and the next policy in the pipeline executes after the policy that caused the error. The advantage to handling the error in the current flow is that you might have a way to recover from the error to complete processing of the request.

Shown below is a VerifyAPIKey policy named verify-api-key with the continueOnError element set to true:

<VerifyAPIKey async="false" continueOnError="true" enabled="true" name="verify-api-key">
  <DisplayName>Verify API Key</DisplayName>
  <APIKey ref="request.queryparam.apikey"/>
</VerifyAPIKey>

If the API key is missing or invalid, then the VerifyAPIKey policy sets the verifyapikey.verify-api-key.failed variable to true, but processing continues in the current flow.

You then add VerifyAPIKey policy as a step in the PreFlow of the ProxyEndpoint:

<ProxyEndpoint name="default">
  ...
  <PreFlow name="PreFlow">
    <Request>
      <Step>
        <Name>verify-api-key</Name>
      </Step>
      <Step>
        <Name>FaultInFlow</Name>
        <Condition>(verifyapikey.verify-api-key.failed = "true")</Condition>
      </Step>
    </Request>
    <Response/>
  </PreFlow>	
</ProxyEndpoint>	

Notice how the next step in the PreFlow uses a condition to test for the existence of an error. If an error occurred in the VerifAPIKey policy, then the policy named FaultInFlow policy executes. Otherwise, the FaultInFlow policy is skipped. The FaultInFlow policy can do many things, such as logging the error, attempting to fix the error, or performing some other action.

Triggering an error by using the Raise Fault policy

You can use the Raise Fault policy at any time in a flow to trigger an error. When a Raise Fault policy executes, it terminates the current flow and transfers control to the error flow.

One use of the Raise Fault policy is to test for a specific condition that another policy might not detect. In the example above, you added a <Condition> tag to a PreFlow <Step> tag that caused the policy FaultInFlow to execute if the condition is met. If FaultInFlow is a Raise Fault policy, then control transfers to the Error flow. Or, you might insert a Raise Fault policy in a flow to debug and test your FaultRules.

When a Raise Fault policy triggers an error, you can use the following FaultRule and condition to process it:

<FaultRule name="raisefault_rule">
  <Step>
    <Name>{policy_name}</Name>
  </Step>
  <Condition>(fault.name = "RaiseFault")</Condition>
</FaultRule>

Note that the condition tests for a fault named RaiseFault.

Custom handling of HTTP error codes from the target server

The examples shown in the previous sections apply to errors created by policies. However you can also create a custom response for transport-level errors, meaning HTTP errors returned from the target server. To control the response from an HTTP error, configure a TargetEndpoint to process HTTP response codes.

By default, Edge treats HTTP response codes in the 1xx-3xx range as 'success', and HTTP response codes in the range 4xx-5xx as 'failure'. That means any response from the backend service with an HTTP response code 4xx-5xx automatically invokes the Error flow, which then returns an error message directly to the requesting client.

You can create custom handlers for any HTTP response codes. For example, you might not want to treat all HTTP response codes in the range 4xx-5xx as 'failure' but only 5xx, or you might want to return custom error messages for HTTP response codes 400 and 500.

In the next example, you use the success.codes property to configure the TargetEndpoint to treat HTTP response codes 400 and 500 as a success, along with the default HTTP codes. By treating those codes as a success, the TargetEndpoint takes over the processing of the response message, instead of invoking the Error flow:

<TargetEndpoint name="default">
  ...
  <HTTPTargetConnection>
    <Properties>
	  <Property name="success.codes">1xx,2xx,3xx,400,500</Property>
    </Properties>
    <URL>http://weather.yahooapis.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

As you can see in this example, you can use wildcards to set the success.codes property to a range of values.. 

Setting the success.codes property overwrites the default values. Therefore, if you want to add HTTP code 400 to the list of default success codes, set this property as:

<Property name="success.codes">1xx,2xx,3xx,400</Property>

But, if you only want HTTP code 400 to be treated as a success code, set the property as:

<Property name="success.codes">400</Property>

You can now define custom handlers for HTTP response codes 400 and 500 to return a customized response message to the requesting app. The following TargetEndpoint uses the policy named ReturnError to handle HTTP 400 and 500 response codes:

<TargetEndpoint name="default">
  <PreFlow name="PreFlow">
    <Request/>
    <Response>
      <Step>
        <Name>ReturnError</Name>
        <Condition>(response.status.code = 400) or (response.status.code = 500)</Condition>
      </Step>
    </Response>
  </PreFlow>

  <HTTPTargetConnection>
    <Properties>
      <Property name="success.codes">1xx,2xx,3xx,400,500</Property>
    </Properties>
    <URL>http://weather.yahooapis.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

This TargetEndpoint configuration causes the policy called ReturnError to handle the response whenever the TargetEndpoint encounters an HTTP response code of 400 or 500.

Pattern for centralized, reusable fault handling

The following Apigee Community post describes a pattern for centralized fault handling without code duplication:

https://community.apigee.com/content/kbentry/23724/an-error-handling-pattern-for-apigee-proxies.html

Fault taxonomy

API Services organizes faults into the following categories and subcategories.

Category Subcategory Fault Name Description
Messaging Failures that occur during the message flow (not including policy failures)
  Custom faults {fault_name} Any faults explicitly handled by the API proxy using the Raise Fault policy
Response codes InternalServerError, NotFound HTTP error codes 5xx, 4xx
Routing failures NoRoutesMatched Failure in selecting a named TargetEndpoint for a request
Classification failures NotFound Failures caused by a request URI that does not match any BasePath for any ProxyEndpoint configurations (that is, no API proxies match the URL in the client app's request)
Transport HTTP transport-level errors
  Connectivity ConnectionRefused, ConnectionReset, ConnectionTimeout Failures occur while establishing network or transport-level connections
Request validations ContentLengthMissing, HostHeaderMissing Faults occur during semantics checks on every request
Response validations Faults occur during semantics checks on every response
IO errors SSLHandshakeError, ReadTimeout, ReadError, WriteTimeout, WriteError, ChunkError Read/write errors at client or target endpoints, timeouts, TLS/SSL errors, and chunked errors
System Undefined runtime errors
  Memory OutOfMemory, GCOverLimit Memory-related failures
Thread RogueTaskTerminated Failures such as termination of run-away tasks
Policy Faults for each Policy type are defined in the Policy Reference. See Policy reference overview.

An error is always accompanied by a text description of the reason for the failure. When the system raises a fault, a set of attributes are populated to assist in troubleshooting. A fault includes the following information:

  • Reason
  • User-defined custom attributes

 

Help or comments?