Logging AJAX responses for errorsfor Drupal 8 , 9 , 10 , and 11

Last updated :  

An AJAX callback that fails is uniquely frustrating to debug, because it often fails silently — no PHP error page, no red text, just a page that quietly doesn't update the way you expected. The request happened somewhere between the browser and the server, and the failure could be on either side of that gap. Tracing it means checking both halves separately, not assuming the bug is wherever you happen to be looking first.

What you'll learn in this lesson

  • Why AJAX failures are harder to notice than a normal PHP error
  • How to log inside an AJAX callback to confirm the server side ran correctly
  • How to use the browser's Network tab to see what actually came back
  • The three places an AJAX failure typically hides: network, server logic, and client-side JavaScript

Where AJAX failures actually come from

  • Network issues — the request never reached the server, or the response never made it back, with no PHP code involved at all.
  • Server-side errors — your callback threw an exception, returned the wrong type, or targeted a wrapper `id` that doesn't match anything on the page.
  • Client-side scripting errors — a JavaScript error elsewhere on the page silently breaks Drupal's own AJAX behaviors before your callback's response is even processed.

Each of these needs a different debugging tool — logging only helps with the middle one.

Logging from inside the callback itself

namespace Drupal\my_module\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;

class AjaxForm extends FormBase {

  public function getFormId() {
    return 'ajax_form_example';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['input'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Enter a value'),
      '#ajax' => [
        'callback' => '::ajaxCallback',
        'wrapper' => 'form-output',
      ],
    ];

    $form['output'] = [
      '#type' => 'markup',
      '#markup' => '
', ]; return $form; } public function ajaxCallback(array &$form, FormStateInterface $form_state) { \Drupal::logger('my_module')->notice('AJAX callback fired with value: @value', [ '@value' => $form_state->getValue('input'), ]); $response = new AjaxResponse(); $response->addCommand(new HtmlCommand('#form-output', $this->t('You entered: @value', [ '@value' => $form_state->getValue('input'), ]))); return $response; } }

If this log entry shows up at `/admin/reports/dblog` after you trigger the AJAX interaction, you've just proven the server side ran and received the value you expected — which immediately rules out "network issue" and "server never got the request" as explanations. If the entry never appears, the problem is somewhere before your PHP even ran.

The wrapper id has to match exactly. The single most common cause of an AJAX callback that "does nothing" isn't a PHP error at all — it's a `#ajax['wrapper']` value that doesn't match any element's actual rendered `id`. The server-side code can run perfectly and still visibly do nothing, because the browser can't find anywhere to put the response.

Checking the other half: the browser's Network tab

Server-side logging only tells you about the server side. Open your browser's developer tools, go to the Network tab, and trigger the AJAX interaction again — you'll see the actual request Drupal's JavaScript sent and the actual response that came back, including the HTTP status code. A 200 response with your logged entry present means the server did its job correctly; anything else (a 500, a 404, no request at all) points you toward the network or JavaScript side instead.

Quick check: your log entry appears correctly every time you trigger the AJAX interaction, but the page never visibly updates. Where's the bug — server-side or client-side? (Answer: client-side, most likely a wrapper `id` mismatch or a JavaScript error elsewhere on the page — the log entry proves your PHP callback ran and returned a response; the problem is in how that response gets applied to the DOM.)

Key takeaways

  • AJAX failures typically fail silently — no PHP error page, just a page that doesn't update — so you have to actively check both the server and client sides.
  • A `\Drupal::logger()` call inside the callback proves whether the server side ran at all, ruling network/routing problems in or out.
  • The browser's Network tab shows the actual request/response cycle, including the HTTP status, independent of anything Drupal's own logs capture.
  • A mismatched `#ajax['wrapper']` id is the single most common cause of a callback that runs correctly but visibly does nothing.

Coming up next

One more debugging tool rounds out this topic: the Devel module itself, which bundles several of the techniques from this section — variable inspection, query logging, and more — into a single toolkit. The final lesson covers using it specifically for form debugging.