How to unify different error/fault format?

Not applicable

We're trying to unify our error responses into one format. This only makes sense if we can align all error responses, especially those generated by Edge and not catchable by individual proxies.

What steps do we have to take to customize all possible errors?


For example, on our on-premise Edge Cloud, if I request a non-existing path I get

{
  "fault": {
    "faultstring": "Classification failed for host: apiproxy-dev-online:9001 url: /foo",
    "detail": {
      "code": "CLASSIFICATION_FAILED"
    }
  }
}

This can be solved by https://community.apigee.com/questions/8072/modify-default-404-response-in-cloud-instance.html - but there surely are more cases to cover.

3 13 1,213
13 REPLIES 13

Not applicable

Sent to early 😮 this probably shouldn't have gone into "General"... sorry.

No Issues @Morris Brodersen , I have moved the post into Private Cloud Deployment category.

@Morris Brodersen , See similar question asked here. Answer by @arghya das gives more details on this issue.

Reposting answer,

The classification error is usually thrown by the router if the basepath doesn't match. The only way to handle this would be to deploy another proxy with a basepath of "/" so that all requests atleast match that and then you can do customized error messages in that proxy using fault handling rules.

In our router, there's a provision to map specific error codes so that you can send custom responses to the caller, but that is only supported for 5xx responses. So like@David Allen suggested, you can go down the path of a custom proxy with "/" basepath so that you never get a 404 Classification Error. You can then return specific responses using Assign Message or Fault Rules policies.

Please feel free to comment if you feel this question is different from the one mentioned to reopen the question.

Thanks for your comment. I forgot to stress that the 404 is only an example - the catch-all proxy is sufficient if 404 is the only error to be expected that cannot be catched by a regular proxy. More examples are e.g. "Node.js still starting" and probably more which I don't know about. I need to align all errors into a simple, unified format. Otherwise all of my clients need to potentially handle multiple formats. Is there a list of possible errors which I cannot catch and normalize in proxies?

Not applicable

I think the point here is not "what" is this error about and how to handle it, but how to unify different error formats.

Is there an easy way, how to apply unified format to all error from an API proxy? For example, if a proxy is implemented in Node.js and protected by an API key. The default error format by the API key policy will be different from the error format from the Node.js app. If I don't want to adopt Apigee's format, I need to somehow modify the default error format.

Yes, that's exactly the question 🙂

Not applicable

Still desperately looking for a solution; surely this is something any API designer would want. Any help is appreciated!

Not applicable

Hi @Morris Brodersen, @Ivan Novakov You can get pretty far, within the context of an API, by creating a <DefaultFaultRule> on both the proxy and the target endpoints. For example:

<ProxyEndpoint name="default">
    <Description/>
    <DefaultFaultRule name="DefaultFaultRule">
        <Step>
            <FaultRules/>
            <Name>Fault-default-proxy</Name>
        </Step>
        <AlwaysEnforce>false</AlwaysEnforce>
    </DefaultFaultRule>
...

The proxy Assign Message Policy:

<AssignMessage enabled="true" continueOnError="false" async="false" name="Fault-default-proxy">
    <DisplayName>Fault default proxy</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Payload contentType="application/json" variablePrefix="%" variableSuffix="#">
            { 
            "proxyFault":
                {
                "faultName":"%fault.name#",
                "faultType":"%fault.type#",
                "faultCategory":"%fault.category#", 
                "errorCode":"%error.code#", 
                "errorMessage":"%error.message#" 
                }
            }</Payload>
        <StatusCode>{message.status.code}</StatusCode>
        <ReasonPhrase>{message.reason.phrase}</ReasonPhrase>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

For the target endpoint:

<TargetEndpoint name="default">
    <DefaultFaultRule name="DefaultFaultRule">
        <Step>
            <FaultRules/>
            <Name>Fault-default-target</Name>
        </Step>
        <AlwaysEnforce>false</AlwaysEnforce>
    </DefaultFaultRule>

...

The target Assign Message policy:

<AssignMessage enabled="true" continueOnError="false" async="false" name="Fault-default-target">
    <DisplayName>Fault default target</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers/>
        <Payload contentType="application/json" variablePrefix="%" variableSuffix="#">
            { "targetFault":
                {
                "faultName":"%fault.name#",
                "faultType":"%fault.type#",
                "faultCategory":"%fault.category#", 
                "errorCode":"%error.code#", 
                "errorMessage":"%error.message#",
                "responseMessageFromBackend":%response.content#
                }
            }</Payload>
        <StatusCode>{message.status.code}</StatusCode>
        <ReasonPhrase>{message.reason.phrase}</ReasonPhrase>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

Then any fault that is created would be handled by the corresponding Assign Message and "unified".

The target side handles the 400 and 500 coming from an HTTPTargetConnection which generates a fault, and "unifies" them.

However, I noticed that 400 and 500 responses coming from a ScriptTarget (Node.js based proxy) do not generate a fault, so you would have to create a rule to raise a fault in those proxies in the target response PostFlow.

Hope this helps

Thanks - this is very helpful since we surely need to apply some error unification for each proxy. I'm still looking for a complete(!) list of errors possible *outside* of proxies, i.e. Edge errors like CLASSIFICATION_FAILED, plus a way to modify each response.

adas
New Member

@Morris Brodersen I think I know what you are looking for. You can use the raise fault policy, to handle any kind of errors, depending on what the error is. You can have default fault rules to catch errors which do not match any specific error, and you can have fault rules which check for specific error codes or error conditions. Unfortunately, none of this can be done at a global level which means the same logic needs to be duplicated in multiple proxies in the absence of global policies or global proxies. So this approach is going to be a little ugly.

Here's an alternate approach:

You can write a javascript which checks for all possible errors that a proxy can generate (you need to get the list of all different error codes and error messages that Apigee Edge generates) and handle each of them in javascript like a switch-case or if-else loop and either set appropriate flow variables or customised error messages. This script can be imported as a global resource (org or env level) so that you do not have to duplicate this logic in all your proxies. Then in each of your proxies, you can have a DefaultRule which contains a javascript policy that refers to this javascript resource file. This way you are not duplicating the entire error handling, but you still need to add the javascript policy and the default fault rule to all your proxies. This is not the elegant way of doing it, but I feel this approach would allow maximum re-use of your fault handling code and allow modularising the fault handling code. The problem that you would have to deal with, is to identify and collect all the different runtime error codes that the Apigee platform and the policies can generate.

I hope this helps. I know this is not a very elegant solution but I would take this as a use case and run it through our product engineering folks to see if we can come up with a better approach of support a feature which facilitates this.

Yes, that helps to reduce duplication. However this will only handle faults generated by proxies, not platform faults, right?

adas
New Member

Mostly proxies, but if there are any platform errors which are not caught in the proxies you could still catch them in the JavaScript code. Like I said, the code might have to be revised based on new errors that you encounter because we do not provide a list of all errors and error codes. I understand this is a drawback in the current system.

That's a nice approach to reusing code across proxies, thanks. It still relies on using a DefaultRule which will catch all proxy policy faults and any raise faults for backend errors using a condition test such as status code >= 400.

The catch-all proxy will catch the classification errors.

So the only platform error I found that "sneaks" through is a syntax error in a node.js file. That generates the following which I don't know how to catch.

{
  "fault": {
    "faultstring": "Script node executed prematurely: unterminated string literal\nunterminated string literal\n    at module.js:439\n    at module.js:474\n    at module.js:356\n    at module.js:312\n    at module.js:497\n    at startup (trireme.js:142)\n    at trireme.js:923\n",
    "detail": {
      "errorcode": "scripts.node.runtime.ScriptExitedError"
    }
  }
}

Are there other platform errors that are sneaking through?