> For the complete documentation index, see [llms.txt](https://help.glpi-project.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.glpi-project.org/tutorials/notifications/queuednotification-analysis.md).

# Analyse "queuednotification" memory\_limit

This procedure should help you determine which ticket/object in GLPI is causing a CRITICAL **memory\_limit** issue when the **queuednotification** automatic action runs.

***

## Example error in `php-errors.log`

```php
[2026-07-17 04:00:09] glpi.CRITICAL:   *** Fatal Error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 9269800 bytes)
  Backtrace :
  ./src/DBmysql.php:596                              

[2026-07-17 09:38:27] glpi.CRITICAL:   *** Uncaught PHP Exception Symfony\Component\ErrorHandler\Error\OutOfMemoryError: "Error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 4775392 bytes)" at DBmysql.php line 596
  Backtrace :
  ./src/DBmysql.php:596
```

These SQL queries should be run against the GLPI database that is throwing the error.

***

## Overview: total size of the queue (`queuednotification`)

```sql
SELECT COUNT(*) AS nb,
       ROUND(SUM(LENGTH(COALESCE(body_html,''))+LENGTH(COALESCE(body_text,'')))/1048576,1) AS total_mb,
       MAX(sent_try) AS max_retry
FROM glpi_queuednotifications WHERE is_deleted = 0;
```

**Interpretation**

* **nb** = how many emails are pending (a queue that keeps growing is a symptom).
* **total\_mb** = raw cumulative weight of all HTML/text bodies (if this runs into hundreds of MB, it's consistent with hitting the `memory_limit`).
* A high **max\_retry** (>3-5) indicates emails that keep failing in a loop and get picked up again in the batch on every cron run, which makes the underlying problem worse.

Concrete example of a worrying result:

<figure><img src="/files/zkd2nW2MXhk2UrxsYa9F" alt=""><figcaption></figcaption></figure>

***

## The 20 largest pending notifications

```sql
SELECT id, itemtype, items_id, event, recipient, sent_try, create_time,
       ROUND(LENGTH(COALESCE(body_html,''))/1048576,2) AS html_mb
FROM glpi_queuednotifications
WHERE is_deleted = 0
ORDER BY LENGTH(COALESCE(body_html,'')) DESC LIMIT 20;
```

**Interpretation**

* **html\_mb**: if one or more rows are several MB (5-15 MB), the associated **`items_id`** is very likely the cause of the error.
* **itemtype/items\_id**: gives you the ticket (or other object) at fault to go inspect.

Concrete example of an abnormal result for **`items_id`** 5557:

<figure><img src="/files/iDG9L07HNw6qYHqbzmWi" alt=""><figcaption></figcaption></figure>

***

## Which ticket/object generates the most volume (`body × recipients`)

```sql
SELECT itemtype, items_id, event, COUNT(*) AS nb_dest,
       ROUND(SUM(LENGTH(COALESCE(body_html,'')))/1048576,1) AS mb
FROM glpi_queuednotifications
WHERE is_deleted = 0
GROUP BY itemtype, items_id, event
ORDER BY mb DESC LIMIT 10;
```

**Interpretation**

* A high **nb\_dest** (10, 20, 30...) combined with a high **mb** = strong likelihood that this **`items_id`** is causing the error.

Concrete example of an abnormal result for **`items_id`** 5557:

<div align="left"><figure><img src="/files/6asAPUQmnk6WSxg4hTAF" alt=""><figcaption></figcaption></figure></div>

***

## Simulating the next cron batch

```sql
SELECT ROUND(SUM(LENGTH(COALESCE(body_html,''))+LENGTH(COALESCE(body_text,'')))/1048576,1) AS mb_next_batch
FROM (SELECT body_html, body_text FROM glpi_queuednotifications
      WHERE is_deleted = 0 AND mode = 'mailing' AND send_time <= NOW()
      ORDER BY send_time ASC LIMIT 50) q;
```

**Interpretation**

* If **mb\_next\_batch** exceeds roughly 100-150 MB, the next cron run will likely crash again (the multiplying effect of the buffered mysqli result plus the PHP copy can triple this figure in actual memory usage).
* If the result is low but the crash persists, it's a sign that the batch has changed between two runs (new emails arrived, or **`sent_try`** caused other rows to resurface) — rerun the query right before/after the crash to compare.

Concrete example of an abnormal result:

<figure><img src="/files/SlCTQOOQ3KDvYoJsgMSV" alt=""><figcaption></figcaption></figure>

***

## Inspecting the offending ticket

Once the candidate ticket for the crash has been identified via SQL, you can go inspect it right away (in SQL or the UI) — there's a good chance the problem will be immediately obvious. Example here with +6000 followups (it can also be very heavy images in followups/descriptions):

<figure><img src="/files/mCy2tgnBnYFmNehAZ2yE" alt=""><figcaption></figcaption></figure>

That many followups is typical of an auto-reply loop: a sender with an autoresponder (out-of-office, marketing email, read receipt...) replies to every GLPI notification, the mail receiver turns each reply into a followup, which triggers a new notification, which in turn causes another automatic reply, and so on.

Each new followup re-embeds the entire quoted history, which is why notifications end up several MB in size and eventually blow up the cron's memory.

***

## Cleaning up the ticket

With thousands of followups, deleting via the interface also risks exceeding the **`memory_limit`**.

So the followups are deleted directly in the database first:

**First check the volume:**

```sql
SELECT COUNT(*)
FROM glpi_itilfollowups
WHERE itemtype = 'Ticket' AND items_id = <DETECTED_TICKET_ID>;
```

**Then delete:**

```sql
DELETE FROM glpi_itilfollowups
WHERE itemtype = 'Ticket' AND items_id = <DETECTED_TICKET_ID>;
```

Once the followups have been purged, the ticket becomes manageable again in the interface: put it in the trash and then permanently purge it from GLPI (not via SQL), so that GLPI properly cleans up all related data (actors, documents, queued notifications...).

You should also remember to purge any notifications still pending for this ticket if the queue queries still show some.

***

## Cleaning up the mail receiver

Connect to the mailbox used by the receiver and delete the remaining loop emails (including in any configured archive/reject folders).

{% hint style="danger" icon="trash" %}
Without this, the next receiver run will re-import the messages and the loop could potentially start again.
{% endhint %}

***

## Adjusting the receiver's rules engine

In **`Administration`** *>* **`Rules`** *>* **`Rules for assigning a ticket created through a mails receiver`**, check that the default rules for rejecting automatic replies are enabled, and supplement them as needed based on the case encountered.

Useful criteria:

* an ***Auto-Submitted*** header different from ***no*** (RFC 3834 standard, covers most autoresponders);
* ***X-Auto-Response-Suppress*** or ***X-Autoreply*** headers present;
* sender addresses such as `no-reply@`, `newsletter@`, `mailer-daemon@`...;
* subject line containing the patterns observed in the loop (in our example, a recurring marketing subject).

The associated action: **reject the ticket without notification** (especially not an automatic rejection reply, which would restart the loop).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://help.glpi-project.org/tutorials/notifications/queuednotification-analysis.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
