Why apex.server.process Success Fires Even on Errors (and How to Handle It)

Introduction

In my previous post, I shared how to implement bulk update/delete in Interactive Grids using apex.server.process and JSON payloads.

In that post, I mentioned a common source of confusion:
👉 “I raised an error in my PL/SQL Ajax Callback… so why did my JavaScript success function still run?”

This post clears up that confusion and shows the right pattern for handling errors with structured JSON.


The Confusion

Let’s imagine you wrote a PL/SQL Ajax Callback like this:

raise_application_error(-20001, 'Select at least one row.');

When you call it from JS:

apex.server.process("PROC", {x01: payload}, {
  success: function(pData){ console.log("Success", pData); },
  error:   function(req, status, err){ console.error("Error", err); }
});

You expected the error handler to run. But instead, the success handler fired (with no useful data). Why?


HTTP Success ≠ Application Success

The key is that apex.server.process treats the HTTP response status as the measure of success/failure.

  • If PL/SQL completes normally (no unhandled exception) → APEX sends back an HTTP 200 OKsuccess callback runs.

  • If PL/SQL crashes (unhandled exception, ORA- error, invalid process) → APEX returns HTTP 500error callback runs.

This means:

  • ✅ Transport-level success (200 OK) → JS success callback always runs.

  • 🚫 Business logic errors (validation, “no rows selected”) → must be handled inside the success callback.


The send_error Pattern

Instead of raising exceptions for business rules, return structured JSON:

PL/SQL:

procedure send_error(p_msg varchar2) is
begin
  apex_json.open_object;
  apex_json.write('ok', false);
  apex_json.open_array('errors');
    apex_json.open_object;
    apex_json.write('type','error');
    apex_json.write('location','page');
    apex_json.write('message', p_msg);
    apex_json.close_object;
  apex_json.close_array;
  apex_json.close_object;
end;

Use it like this:

if l_size = 0 then
  send_error('Select at least one row.');
  return;
end if;

Client JS:

apex.server.process("PROC", { x01: payload }, {
  success: function(pData){
    if (pData && pData.ok === false) {
      apex.message.showErrors(pData.errors);
      return; // stop success path
    }
    // normal success path
  },
  error: function(req, status, err){
    apex.message.showErrors([{type:'error', location:'page', message:'Server error: ' + err}]);
  }
});

When the error Callback Really Fires

The error: handler only triggers when the HTTP request fails:

  • Network issues

  • Server unreachable

  • Unhandled PL/SQL exception → HTTP 500

  • Invalid process name


Visual Recap

  • 200 OK + ok:true → success path

  • 200 OK + ok:false → validation error → showErrors

  • 500 Error → technical failure → error handler


Key Takeaways

  • apex.server.process success callback means “HTTP OK,” not “business OK.”

  • For business/validation rules → return structured JSON (ok:false, errors[]).

  • For technical crashes → error callback handles HTTP 500/404.

  • Always separate transport success from application success.


Closing

This small but important distinction helps avoid a lot of head-scratching in APEX apps.

👉 Next up, I’ll share more practical use cases for APEX Collections and how they can power real-world features like carts and wizards.

0
Subscribe to my newsletter

Read articles from Bhuvnesh Singh Chauhan (bhuvi_champ) directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bhuvnesh Singh Chauhan (bhuvi_champ)
Bhuvnesh Singh Chauhan (bhuvi_champ)