Discover v2009: Error handling

Wednesday, April 14, 2010


As with any programming system, error handling is a critical part of the AdWords API as well. In v13, we used to describe every error by its own error code. While this was simple and handy, the API provided less additional information about the error itself. In v2009, we decided instead to use error types with additional fields to describe the specifics of each error. The new error handling system gives you the type of error (e.g. PolicyViolationError), the cause of error (e.g. the editorial policy that was violated), the field that triggered the error and so forth. In this blog post, I'll cover the v2009 error handling in detail, with focus on v2009 synchronous services.

Error handling in v13 and v2009 APIs

To show the difference in error handling between v13 and v2009, consider the following v13 code to create a campaign.

try {
AdWordsUser user = new AdWordsUser();
CampaignInterface service =
(CampaignInterface) user.getService(AdWordsService.V13.CAMPAIGN_SERVICE);
Campaign campaign = new Campaign();
campaign.setName("New campaign");
campaign.setBudgetPeriod(BudgetPeriod.Daily);
campaign.setBudgetAmount(50L);
campaign = service.addCampaign(campaign);
} catch (ApiException e) {
for (ApiError error : e.getErrors()) {
System.out.println("There is an error on argument " + error.getIndex()
+ ". Error code is " + error.getCode()
+ ", details are \"" + error.getDetail()
+ "\", field is " + error.getField());
}
}

This code generates the following output:

There is an error on argument 0. Error code is 34, details are "Money amount less than CurrencyMinCpc.", field is budget.

The same code in v2009 is given below:

try {
user = new AdWordsUser();
CampaignServiceInterface service = (CampaignServiceInterface)
user.getService(AdWordsService.V200909.CAMPAIGN_SERVICE);
Campaign campaign = new Campaign();
campaign.setName("New campaign");
campaign.setBiddingStrategy(new ManualCPC());
campaign.setBudget(new Budget(BudgetBudgetPeriod.DAILY,
new Money(null, 50L), BudgetBudgetDeliveryMethod.STANDARD));
CampaignOperation operation = new CampaignOperation();
operation.setOperator(Operator.ADD);
operation.setOperand(campaign);
campaign = service.mutate(new CampaignOperation[] {operation}).getValue()[0];
} catch (ApiException e) {
for (ApiError error : e.getErrors()) {
System.out.println("There is an error on argument " + error.getFieldPath()
+ ".");
if (error instanceof BudgetError) {
BudgetError budgetError = (BudgetError) error;
System.out.println("Reason is " + budgetError.getReason());
}
}
}

The code generates the following output:

There is an error on argument operations[0].operand.budget. Reason is NON_MULTIPLE_OF_MINIMUM_CURRENCY_UNIT.

As shown in the code above, v2009 returns an error object derived from ApiError, with an appropriate error reason that gives more insight into why the error occurred. This allows you to write a switch-case to handle error categories and reasons that are significant for your program more elegantly than in v13. Also, the list of exceptions raised by a service are available along with the service documentation. For instance, all the exceptions that can possibly be raised by CampaignService are listed at http://code.google.com/apis/adwords/v2009/docs/reference/CampaignService.html#errors

Retrieving the violating field using FieldPath

FieldPath is another useful item you get from a v2009 error. FieldPath stores the OGNL path to the field that caused the violation. OGNL stands for Object-Graph Navigation Language; it is an expression language for getting and setting properties of objects. You can use it as a debugging aid while developing your application, retrieve the value of the violating field, etc. For instance, the following code shows how to retrieve the value of budget that caused the error using the library from opensymphony.

try {
OgnlContext ognlContext = new OgnlContext();
ognlContext.put("operations", operations);
Object value = Ognl.getValue(error.getFieldPath(), ognlContext);
System.out.println("Violating field is " + error.getFieldPath()
+ " and value is " + value);
} catch (OgnlException e) {
e.printStackTrace();
}

This code will print the output as

Violating field is operations[0].operand.budget and value is 50.

In case you are interested in just the index of the violating operation, you can evaluate it using a simple regex as follows:

public static int getOperationsIndex(String ognl) {
String OPERATIONS_INDEX_REGEX = "^operations\\[(\\d+)\\]";
Matcher m = Pattern.compile(OPERATIONS_INDEX_REGEX).matcher(ognl);
if (m.find()) {
return Integer.parseInt(m.group(1));
} else {
return -1;
}
}

Validating API calls using validateOnly

AdWords API also allows you to validate your API calls to see if the request will generate any errors or not. You can validate any API call by setting the validateOnly SOAP header to true and making the API call as usual. If the request contains errors, you will get ApiError objects. If the request is fine, no errors are thrown, and the API call doesn't perform any action either. In addition to providing a way to validate your API calls, validateOnly is significantly cheaper than normal calls. You can refer to the rate sheet to calculate how many API units a validateOnly call will cost you. For more details on how to use validateOnly headers, you can refer to our blog post here

In our next blog post, we will cover how v2009 error handling works in BulkMutateJobService. We've included support for error handling in all of our client libraries to help get you started, so please try it out and share your feedback with us on the forum.

-- Anash P. Oommen, AdWords API Team