---
title: "Event diagnostics"
description: "Every problem Togo reports on an event — refused, dropped or kept but wrong — with what it means and the fix for your coding agent."
canonical: "https://www.togohq.ai/docs/reference/event-diagnostics"
applies_to: "in.togohq.ai, mcp.togohq.ai/mcp, @togohq/sdk-browser 1.x, @togohq/sdk-node 1.x"
updated: "2026-11-04T00:00:00Z"
---

# Event diagnostics

Every problem Togo reports on an event — refused, dropped or kept but wrong — with what it means and the fix for your coding agent.

Togo checks each event when it arrives and reports what is wrong with it as a **diagnostic**: a code, a severity, what it means, and the fix. Live View shows them on each event, and the `verify_tracking` MCP tool returns them to your coding agent with the same text you see here.

An **error** means production drops or loses the data: the event was refused when it arrived, or a part of it will not be stored. A **warning** means production keeps the event as sent, but it is not what its event definition says, or it is very likely a bug.

Events refused when they arrive are also named in the ingest response, and the SDKs log each one as a `[togo]` error, so the console or your server log is the first place to look:

```json
{ "accepted": 19, "rejected": [{ "eventId": "…", "reason": "timestamp_too_old" }] }
```

A code is permanent: its meaning and its severity never change.

| Code                                                                    | Severity | What it is                                             |
| ----------------------------------------------------------------------- | -------- | ------------------------------------------------------ |
| [`timestamp_too_far_future`](#timestamp_too_far_future)                 | error    | Dated in the future                                    |
| [`timestamp_too_old`](#timestamp_too_old)                               | error    | Older than 7 days                                      |
| [`envelope_property_value_too_long`](#envelope_property_value_too_long) | error    | A property value is over 1,024 characters              |
| [`envelope_too_many_properties`](#envelope_too_many_properties)         | error    | More than 256 properties                               |
| [`session_id_invalid`](#session_id_invalid)                             | error    | The session id is not valid                            |
| [`event_name_reserved`](#event_name_reserved)                           | error    | The name starts with $                                 |
| [`identity_missing`](#identity_missing)                                 | error    | No userId or anonymousId                               |
| [`event_rejected`](#event_rejected)                                     | error    | Refused when it arrived                                |
| [`event_not_defined`](#event_not_defined)                               | warning  | The event has no definition                            |
| [`event_not_defined_dropped`](#event_not_defined_dropped)               | error    | Dropped: the event has no definition                   |
| [`property_not_defined`](#property_not_defined)                         | warning  | A property is not in the definition                    |
| [`required_property_missing`](#required_property_missing)               | warning  | A required property is missing                         |
| [`invalid_property_type`](#invalid_property_type)                       | warning  | A property has the wrong type                          |
| [`invalid_enum_value`](#invalid_enum_value)                             | warning  | A value is not one of the allowed values               |
| [`property_value_too_long`](#property_value_too_long)                   | warning  | A value is longer than its definition allows           |
| [`event_deprecated`](#event_deprecated)                                 | warning  | The event is deprecated                                |
| [`property_deprecated`](#property_deprecated)                           | warning  | A property is deprecated                               |
| [`event_name_near_duplicate`](#event_name_near_duplicate)               | warning  | Looks like an existing event                           |
| [`event_name_contains_email`](#event_name_contains_email)               | error    | The name contains an email address                     |
| [`event_name_too_long`](#event_name_too_long)                           | error    | The name is over 64 characters                         |
| [`event_name_cap_reached`](#event_name_cap_reached)                     | error    | The project has 1,000 event names                      |
| [`property_pii_dropped`](#property_pii_dropped)                         | error    | Dropped: contains an email address                     |
| [`property_contains_email`](#property_contains_email)                   | warning  | Contains an email address                              |
| [`session_id_contains_email`](#session_id_contains_email)               | warning  | The session id is an email address                     |
| [`user_id_contains_email`](#user_id_contains_email)                     | warning  | The userId is an email address                         |
| [`identify_refused`](#identify_refused)                                 | error    | identify() would be refused                            |
| [`person_property_not_defined`](#person_property_not_defined)           | error    | Dropped: the profile property has no definition        |
| [`person_property_key_invalid`](#person_property_key_invalid)           | error    | Dropped: the profile property name cannot be defined   |
| [`person_property_invalid_value`](#person_property_invalid_value)       | error    | Dropped: the profile value does not fit its definition |
| [`person_property_set_once_null`](#person_property_set_once_null)       | error    | Dropped: setOnce with no value                         |
| [`event_id_reused`](#event_id_reused)                                   | error    | Two different events share an eventId                  |
| [`clock_skew_large`](#clock_skew_large)                                 | warning  | The sending clock is more than an hour off             |
| [`live_key_from_localhost`](#live_key_from_localhost)                   | warning  | A local build is sending with the live key             |

## `timestamp_too_far_future`

**Error** — production drops or loses the data. Dated in the future.

The event is dated more than 5 minutes after Togo received it, even after correcting for the device clock. It was refused and not stored. The SDKs stamp events themselves, so this nearly always comes from a client that sets its own timestamp.

**Fix, for your coding agent:** Find where the code that sends the event named in `event` sets its timestamp. Send the time the event happened, in UTC ISO-8601 with a `Z` suffix, or leave the timestamp out so the SDK stamps it. Never send a time computed in a local time zone or a time in the future. Then call `verify_tracking` again.

## `timestamp_too_old`

**Error** — production drops or loses the data. Older than 7 days.

The event happened more than 7 days before Togo received it. It was refused and not stored. Usually a device that was offline for over a week, or old data sent through the live endpoint as a backfill.

**Fix, for your coding agent:** Check the timestamp the code sends with the event named in `event`. If it is a fixed or default date, remove it and let the SDK stamp the event when it happens. If this is a backfill of historical data, stop: the ingest endpoint accepts events up to 7 days old and nothing older. Then call `verify_tracking` again.

## `envelope_property_value_too_long`

**Error** — production drops or loses the data. A property value is over 1,024 characters.

A string property value is longer than 1,024 characters, the platform ceiling. The event was refused and not stored. Long values are nearly always text a person typed, which does not belong in an event.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and the string property that can exceed 1,024 characters. Remove it, or replace it with a short category or a length bucket. Never send text a person typed as an event property. Then call `verify_tracking` again.

## `envelope_too_many_properties`

**Error** — production drops or loses the data. More than 256 properties.

The event carries more than 256 properties, the platform ceiling. It was refused and not stored.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event`. It is probably spreading a whole object into the properties. Send only the properties its event definition lists, and move facts about the person to `setPersonProperties()`. Split an event that describes several things into one event per thing. Then call `verify_tracking` again.

## `session_id_invalid`

**Error** — production drops or loses the data. The session id is not valid.

A custom session id is not 1–128 characters of letters, digits, `_`, `.`, `:`, `@`, `+`, `%` or `-`. The event was refused and not stored.

**Fix, for your coding agent:** Find where the code passes a custom `sessionId` to the SDK. Pass an opaque id from your own session store, 1–128 characters of letters, digits, `_`, `.`, `:`, `@`, `+`, `%` or `-` — no spaces or slashes. If the app has no session store of its own, stop passing `sessionId` and let the SDK manage sessions. Then call `verify_tracking` again.

## `event_name_reserved`

**Error** — production drops or loses the data. The name starts with $.

Event names starting with `$` are reserved for Togo. The event was refused and not stored. This is usually another analytics tool’s automatic events — PostHog’s `$pageview`, for example — being forwarded to Togo.

**Fix, for your coding agent:** Find what sends the event named in `event`. If another analytics tool forwards its automatic events to Togo, stop the forwarding and call Togo directly. If the code chose the name, rename it in lowercase `snake_case` without the `$`, and write its event definition with `update_event_definitions` before sending it. Then call `verify_tracking` again.

## `identity_missing`

**Error** — production drops or loses the data. No userId or anonymousId.

The event has neither a `userId` nor an `anonymousId`, so no person can be credited with it. It was refused and not stored. The browser SDK always sends an `anonymousId` and the server SDK drops such an event itself, so this comes from other clients.

**Fix, for your coding agent:** Find the code that sends the event named in `event`. If it calls Togo over HTTP directly, use the Togo SDK for that runtime instead. If it uses `@togohq/sdk-node`, pass the signed-in person’s `userId`, or the `anonymousId` the browser sent, on every `track()` call. Then call `verify_tracking` again.

## `event_rejected`

**Error** — production drops or loses the data. Refused when it arrived.

The event was refused when it arrived and was not stored, for a reason this view does not describe yet. The event’s rejection code names the reason.

**Fix, for your coding agent:** Read the rejection code on the event named in `event` — the ingest response carries it too, and the SDK logs it to the console — and look it up on the event diagnostics page, which lists every code with its fix. Change the call that sends the event to match. Then call `verify_tracking` again.

## `event_not_defined`

**Warning** — production keeps the data as sent. The event has no definition.

No event definition covers this event. It is kept and can be queried, but it has no description, no property types and nothing to check it against, so the analyst knows only its name.

**Fix, for your coding agent:** Write an event definition for the event named in `event` with `update_event_definitions`: what it means, when it fires, when it does not, and each property it carries with its type. Use the name exactly as the code sends it. Do not rename the event in the code to match an existing definition unless they mean the same thing. Then call `verify_tracking` again.

## `event_not_defined_dropped`

**Error** — production drops or loses the data. Dropped: the event has no definition.

This project keeps only defined events (strict event governance), and no event definition covers this one. Production drops it, and turning the setting off later does not recover it.

**Fix, for your coding agent:** Write an event definition for the event named in `event` with `update_event_definitions` before the code sends it again: what it means, when it fires, when it does not, and each property with its type. If the event should not exist, remove the `track()` call instead. Never turn strict event governance off without asking the person. Then call `verify_tracking` again.

## `property_not_defined`

**Warning** — production keeps the data as sent. A property is not in the definition.

The event has a definition, and the property named in the diagnostic is not in it. The value is kept as sent, with no type or description.

**Fix, for your coding agent:** Compare the `track()` call for the event named in `event` with its event definition (`get_event`). If the property named in `property` is a typo for a defined one, fix the key in the code. If it is new and meant, add it to the definition with `update_event_definitions`, with its type and description. Then call `verify_tracking` again.

## `required_property_missing`

**Warning** — production keeps the data as sent. A required property is missing.

The event definition marks the property named in the diagnostic as required, and the event did not carry it. The event is kept; charts that filter or group by that property miss it.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and send the property named in `property` on every call, including every code path that fires the event. Check the definition with `get_event` for its type. Do not remove `required` from the definition unless the person confirms the property is optional. Then call `verify_tracking` again.

## `invalid_property_type`

**Warning** — production keeps the data as sent. A property has the wrong type.

The property named in the diagnostic was sent with a different type than its definition says — a number as a string, for example, or a timestamp that is not UTC. The value is kept as sent, so charts that use it as the defined type get it wrong.

**Fix, for your coding agent:** The property named in `property` was sent as `actualType`, but its definition expects `expected`. Find the `track()` call for the event named in `event` and send the value as `expected`; a timestamp is UTC ISO-8601 with a `Z` suffix. Do not change the definition to match the code unless the person confirms the definition is wrong. Then call `verify_tracking` again.

## `invalid_enum_value`

**Warning** — production keeps the data as sent. A value is not one of the allowed values.

The property named in the diagnostic has a fixed list of allowed values in its definition, and the event carried a value outside it. The value is kept as sent, and shows as its own category in every chart.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and send only the allowed values listed in `expected` for the property named in `property` — check capitalisation and spelling. If the value is a real new case, ask the person before adding it to the definition with `update_event_definitions`. Then call `verify_tracking` again.

## `property_value_too_long`

**Warning** — production keeps the data as sent. A value is longer than its definition allows.

A string value is longer than the maximum length its property definition sets. The value is kept as sent.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and shorten the value of the property named in `property` to the length its definition allows — send a category or a length bucket instead of text. Never send text a person typed as an event property. Do not raise the definition’s limit unless the person asks. Then call `verify_tracking` again.

## `event_deprecated`

**Warning** — production keeps the data as sent. The event is deprecated.

The event definition is marked deprecated. The event is still kept, but its definition says to send another event instead.

**Fix, for your coding agent:** Replace the `track()` call for the event named in `event` with the event named in `expected`, and carry its properties over as that event’s definition lists them (`get_event`). Remove the old call once the new one is in place; do not send both. If `expected` is empty, ask the person what replaces it. Then call `verify_tracking` again.

## `property_deprecated`

**Warning** — production keeps the data as sent. A property is deprecated.

The property named in the diagnostic is marked deprecated in the event definition. Its value is still kept, but the definition says to stop sending it.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and stop sending the property named in `property`. Read the event definition with `get_event`: if another property replaces it, send that one instead. Do not remove the property from the definition; a deprecated property stays defined so older builds keep working. Then call `verify_tracking` again.

## `event_name_near_duplicate`

**Warning** — production keeps the data as sent. Looks like an existing event.

This name is new, and the key it gets looks like an existing event’s key — `Sign up` beside `sign_up`, for example. It is kept as a separate event, so the two are counted apart until someone merges them.

**Fix, for your coding agent:** The event named in `event` looks like the existing event key in `expected`. If they mean the same thing, change the code to send the existing event’s name exactly — `list_events` shows it. If they are different events, write a definition for the new one with `update_event_definitions` that says how it differs. Then call `verify_tracking` again.

## `event_name_contains_email`

**Error** — production drops or loses the data. The name contains an email address.

The event name looks like it contains an email address — usually a value used as a name. The event is stored but can never be queried by name, and the name is not recorded.

**Fix, for your coding agent:** Find the `track()` call that builds the event name named in `event` from data. An event name is a fixed string in lowercase `snake_case` that says what happened, such as `invite_sent`. Move any per-person value out of the name, and never send an email address in an event at all. Then call `verify_tracking` again.

## `event_name_too_long`

**Error** — production drops or loses the data. The name is over 64 characters.

The event name is longer than 64 characters, the longest a defined name can be — usually a value used as a name. The event is stored but can never be queried by name, and the name is not recorded.

**Fix, for your coding agent:** Find the `track()` call that sends the event named in `event`. An event name is a short fixed string in lowercase `snake_case` that says what happened, such as `report_exported`. If the name is built from data, use one fixed name and send the varying part as a property. Then call `verify_tracking` again.

## `event_name_cap_reached`

**Error** — production drops or loses the data. The project has 1,000 event names.

The project already has 1,000 event names, so this new one is not recorded. The event is stored but cannot be queried by name. A project reaching the cap is nearly always building names from data.

**Fix, for your coding agent:** Find the code that builds event names from data — a page path, an id or a value inside the name. Use one fixed event name and send the varying part as a property instead, for example `page_viewed` with `path`. Ask the person to merge or remove the generated names in the dashboard’s Event registry. Then call `verify_tracking` again.

## `property_pii_dropped`

**Error** — production drops or loses the data. Dropped: contains an email address.

This project has the email filter on, and the property named in the diagnostic has an email address in its key or value. Production drops that property and keeps the rest of the event.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and remove the property named in `property`. An email address belongs on the person, set with `identify()` or `setPersonProperties()`, never on an event. If you need to know whether one was given, send a boolean instead. Then call `verify_tracking` again.

## `property_contains_email`

**Warning** — production keeps the data as sent. Contains an email address.

The property named in the diagnostic has an email address in its key or value. The email filter is off for this project, so it is stored on the event row. Event properties are for booleans, counts, categories and lengths — never personal data.

**Fix, for your coding agent:** Find the `track()` call for the event named in `event` and remove the property named in `property`. An email address belongs on the person, set with `identify()` or `setPersonProperties()`, never on an event. If you need to know whether one was given, send a boolean instead. Then call `verify_tracking` again.

## `session_id_contains_email`

**Warning** — production keeps the data as sent. The session id is an email address.

The custom session id sent with this test event contains an email address. A session id is stored on every event of the session, so the address would be stored in every one of them.

**Fix, for your coding agent:** Find where the code passes a custom `sessionId` to the SDK and pass an opaque id from your own session store instead — a random value, never the signed-in person’s email address. If the app has no session store of its own, stop passing `sessionId` and let the SDK manage sessions. Then call `verify_tracking` again.

## `user_id_contains_email`

**Warning** — production keeps the data as sent. The userId is an email address.

The `userId` sent with the event contains an email address. A `userId` is stored on every event the person sends, so the address is stored in every one of them.

**Fix, for your coding agent:** Find where the code calls `identify()` or passes `userId`, and pass the app’s own stable account id — the database primary key — instead of the email address. Set the email address on the person with `setPersonProperties()` if the person wants it in Togo. Then call `verify_tracking` again.

## `identify_refused`

**Error** — production drops or loses the data. identify() would be refused.

This device’s person already belongs to a different `userId`, so the call is refused rather than merging two people. Production keeps the events but does not link this `userId`.

**Fix, for your coding agent:** Two different users signed in on the same device without a sign-out between them. Find the app’s sign-out code and call the SDK’s `reset()` there, so the next sign-in starts a new person. Then sign out, sign in as a different user and call `verify_tracking` with `expectIdentify` again.

## `person_property_not_defined`

**Error** — production drops or loses the data. Dropped: the profile property has no definition.

A profile property sent with `setPersonProperties()` has no definition. Production drops it and keeps the others.

**Fix, for your coding agent:** Define the person property named in `property` with `update_person_property`, giving its type and what it means, and then send it again; values sent before it was defined are not recovered. If it was a typo for an existing person property, `list_person_properties` shows the defined names; fix the key in the code. Then call `verify_tracking` again.

## `person_property_key_invalid`

**Error** — production drops or loses the data. Dropped: the profile property name cannot be defined.

A profile property sent with `setPersonProperties()` has a name that is not lowercase snake_case, so it can never be defined. Production drops it and keeps the others.

**Fix, for your coding agent:** Find the `setPersonProperties()` call that sends the key named in `property` and rename it in the code to the name in `expected` — a person property name is lowercase snake_case, at most 64 characters. Then, unless `list_person_properties` already lists that name (`email` and `name` always are), define it with `update_person_property`, giving its type and what it means. Do not try to define the old name. Then call `verify_tracking` again.

## `person_property_invalid_value`

**Error** — production drops or loses the data. Dropped: the profile value does not fit its definition.

A profile property was sent with a value its definition refuses — the wrong type, a value outside its allowed list, or one that is too long. Production drops it and keeps the others.

**Fix, for your coding agent:** Find the `setPersonProperties()` call that sends the person property named in `property`, and send a value that fits its definition: `list_person_properties` shows its type and allowed values. Do not change the definition to fit the code unless the person confirms the definition is wrong. Then call `verify_tracking` again.

## `person_property_set_once_null`

**Error** — production drops or loses the data. Dropped: setOnce with no value.

A profile property was sent with `setOnce` and no value. There is nothing to set, so production drops it.

**Fix, for your coding agent:** Find the `setPersonProperties()` call that sends the person property named in `property` with `setOnce`. Only call it when there is a value — check for `null` or `undefined` first — or use `unset` if the intent was to clear the property. Then call `verify_tracking` again.

## `event_id_reused`

**Error** — production drops or loses the data. Two different events share an eventId.

Two events with different contents arrived with the same `eventId`. Togo keeps one event per `eventId` — a retry is meant to reuse it — so production keeps only one of them.

**Fix, for your coding agent:** Find the code that sets `eventId` for the event named in `event`. Leave `eventId` to the SDK, or generate a new random id for every distinct event and reuse it only when retrying the same event. Never derive it from a name, a timestamp or a counter that can repeat. Then call `verify_tracking` again.

## `clock_skew_large`

**Warning** — production keeps the data as sent. The sending clock is more than an hour off.

The time the client said it sent the batch is more than an hour away from when Togo received it. Togo corrects for it, so the event is kept at the right time, but from a server or your own client it usually means timestamps are being set wrongly.

**Fix, for your coding agent:** Find the code that sends the event named in `event`. If it calls Togo over HTTP directly, switch to the Togo SDK for that runtime, which sets both timestamps itself. If it uses an SDK, check it does not pass a timestamp of its own, and that the server’s clock is synchronised. Then call `verify_tracking` again.

## `live_key_from_localhost`

**Warning** — production keeps the data as sent. A local build is sending with the live key.

The event was sent with the live write key from a page on `localhost` or a local address. It is counted as production traffic, so development clicks appear in the real numbers.

**Fix, for your coding agent:** Use the write-key pair’s test key, starting `togo_pkt_`, in development and the live key, starting `togo_pkl_`, only in the production build. Read the key from the build’s environment rather than hard-coding it, and check which value the local environment file sets. Then call `verify_tracking` again.

## HTTP errors

These refuse the whole request rather than one event, so they come back as the response status, not as a diagnostic.

| Status | `error`               | Means                                                         | Retry?                                                                 |
| ------ | --------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `400`  | `invalid_json`        | The body is not JSON                                          | No — fix the client                                                    |
| `400`  | `invalid_batch`       | The batch does not have the expected shape; `issues` says how | No — fix the client                                                    |
| `401`  | `missing_write_key`   | No write key was sent                                         | No                                                                     |
| `401`  | `invalid_write_key`   | The key is not one of Togo's, or it was revoked               | No — see [The write key was rejected](/docs/troubleshooting/write-key) |
| `413`  | `batch_too_large`     | The batch is over 1 MB                                        | No — send smaller batches                                              |
| `429`  | `rate_limited`        | The project sent too much too fast                            | Yes, after `Retry-After`                                               |
| `503`  | `enqueue_failed`      | Togo could not accept the batch right now                     | Yes, with the same event ids                                           |
| `503`  | `inspect_unavailable` | A test-key batch could not reach the test views               | Yes, with the same event ids                                           |

The SDKs retry `429` and `5xx` responses for you, keeping each event's id so a retried event is recognised as the same event, and never retry a `4xx` that would fail the same way again. A `503` never means the events were kept: retry them.
