Verify React Native SDK events
이 페이지는 아직 귀하의 언어로 번역되지 않았습니다. 현재 작업 중이므로 곧 다시 확인해 주십시오.
An event moves through four stages between your track() call and Amplitude's servers. Verify each stage in order. The first stage where the event looks wrong is where the problem is.
| Stage | Question | Where to look |
|---|---|---|
| Captured | Did the SDK accept the call? | Debug logs in the Metro terminal or device log |
| Transformed | What did plugins add, change, or drop? | Verbose logs, or an inspection plugin |
| Sent | Did the request leave the device, and with what payload? | The final-event log line, a proxy, or a custom transport |
| Received | Did Amplitude accept and store the event? | The upload result, then User Lookup in Amplitude |
For problems common to every Amplitude SDK, go to SDK troubleshooting and debugging. If behavior changed after an SDK upgrade, check the React Native SDK release notes.
Turn on debug logging
Set logLevel to Debug while you verify. Debug logs include every public method call, the internal state before and after it, each plugin step, and the server response.
amplitude.init("AMPLITUDE_API_KEY", undefined, {
logLevel: amplitude.Types.LogLevel.Debug,
});
The levels are None (0), Error (1), Warn (2, the default), Verbose (3), and Debug (4). The SDK writes to console, so messages appear in the Metro terminal, in the React Native DevTools console, and in Xcode or Logcat. Each message starts with Amplitude Logger, so filter on that prefix.
Return logLevel to Warn before you ship. Debug output includes event payloads and identifiers.
Check what the SDK captured
With debug logging on, each public call prints a block like this:
Amplitude Logger [Debug]: {
"type": "invoke public method",
"name": "track",
"args": ["Sign Up Clicked", { "plan": "starter" }],
"stacktrace": ["at onPress (SignUpScreen.tsx:42:13)", "..."],
"time": { "start": "2026-11-05T22:59:41.957Z", "end": "2026-11-05T22:59:41.989Z" },
"states": {
"before": { "config.apiKey": "...", "timeline.queue.length": 0 },
"after": { "config.apiKey": "...", "timeline.queue.length": 0 }
}
}
For methods that return a promise, such as track(), identify(), and flush(), the SDK prints the block after the promise resolves. A track block therefore appears after the upload attempt.
Confirm three things:
- The call ran. A
trackblock appears. If not, either your code didn't reach the call orinit()never ran on the instance you called. A second instance fromcreateInstance()needs its owninit(). - The arguments are right.
argsshows the event name and properties exactly as the SDK received them. - The SDK was ready. Calls made before
init()completes don't fail. They wait in a queue and run afterinit(), so a call that appears early can still send later.
At this stage you can also read the identity the SDK attaches to every event:
amplitude.getDeviceId(); // "64b29057-7f8c-..."
amplitude.getUserId(); // "user-12345" or undefined
amplitude.getSessionId(); // 1793919426773
Check how plugins transformed the event
After capture, each event passes through the plugin pipeline in a fixed order:
- Before plugins (
type: "before"): The built-in context plugin addsdevice_id,user_id,session_id,time,insert_id,library,app_version, and the device fields thattrackingOptionsallows, such asplatform,os_name,device_model,language, andip. On iOS and Android, the SDK's native module supplies the device fields. The network checker pauses uploads while the device is offline. - Enrichment plugins (
type: "enrichment", the default): Your own enrichment plugins run here, in the order you add them. - Destination plugins (
type: "destination"): The built-inamplitudedestination queues the event for upload.
A before or enrichment plugin that returns null drops the event. To find the plugin responsible for a change or a drop, set logLevel to Verbose or Debug. The SDK then logs each step:
Amplitude Logger [Log]: Timeline.apply: Event after before plugin '@amplitude/plugin-context-react-native', event: {...}
Amplitude Logger [Log]: Timeline.apply: Final event before destinations, event: {...}
The Final event before destinations line is the exact event the SDK sends. Compare it with what you expect.
To inspect events in code, for example in an automated test, add a destination plugin that records each event without sending it anywhere:
const captured: amplitude.Types.Event[] = [];
amplitude.add({
name: "inspect-events",
type: "destination",
execute: async (event) => {
captured.push({ ...event });
return { event, code: 200, message: "inspected" };
},
});
Destination plugins receive the event after every before and enrichment plugin has run. Remove the plugin with amplitude.remove("inspect-events") when you're done.
Check what the SDK sent
The SDK uploads events with a POST request from JavaScript fetch:
| Configuration | Request URL |
|---|---|
| Default (US) | https://api2.amplitude.com/2/httpapi |
serverZone: "EU" | https://api.eu.amplitude.com/2/httpapi |
useBatch: true (US) | https://api2.amplitude.com/batch |
useBatch: true (EU) | https://api.eu.amplitude.com/batch |
serverUrl set | The URL you set |
Requests leave the device flushIntervalMillis (1 second by default) after the first event enters the queue. Each request carries up to flushQueueSize (30 by default) events. To send right away, call await amplitude.flush().promise.
To inspect the request itself, use one of these tools:
- The Network panel in React Native DevTools, which lists
fetchrequests from JavaScript. - A local HTTP proxy, such as Charles or Proxyman, with the device or simulator configured to use it.
- A custom
transportProviderthat logs the URL and payload before it sends. - For react-native-web, the browser Network tab.
The request body has this shape:
{
"api_key": "AMPLITUDE_API_KEY",
"events": [
{
"event_type": "Sign Up Clicked",
"event_properties": { "plan": "starter" },
"device_id": "64b29057-7f8c-...",
"session_id": 1793919426773,
"time": 1793919426774,
"event_id": 1,
"insert_id": "f393d9ad-...",
"app_version": "1.0.0",
"platform": "iOS",
"os_name": "ios",
"os_version": "17.0",
"device_manufacturer": "Apple",
"device_model": "iPhone15,2",
"language": "en-US",
"ip": "$remote",
"library": "amplitude-react-native-ts/1.13.1"
}
],
"options": {},
"client_upload_time": "2026-11-05T22:59:41.990Z"
}
"ip": "$remote" tells Amplitude to use the request's IP address. Set trackingOptions.ipAddress to false to omit it. When you set minIdLength, options carries it as min_id_length.
Compressed request bodies
For Amplitude's own endpoints, the SDK gzip-compresses larger request bodies only when the JavaScript runtime provides CompressionStream. Hermes doesn't, so requests from iOS and Android apps are plain JSON. On react-native-web, compressed requests carry a Content-Encoding: gzip header, and the Network tab shows binary data. To read those events, use the Final event before destinations log line or an inspection plugin instead.
If no request appears:
| Cause | How to confirm | Fix |
|---|---|---|
| The device is offline | When connectivity changes, debug logs show Network connectivity status: offline. | Expected. The SDK keeps the events and uploads them when the device reconnects. |
You set offline to true | offline: true in your init options. | Remove it. To turn off automatic offline detection, set offline: null instead. |
| Opt-out is on | Every result is 0, Event skipped due to optOut config. | Call amplitude.setOptOut(false). |
| The flush interval hasn't passed | You set a large flushIntervalMillis. | Wait for the interval, or call amplitude.flush(). |
Check what Amplitude received
Read the upload result
Every tracking method returns { promise }. The promise resolves to a Result after the upload attempt finishes:
const { event, code, message } =
await amplitude.track("Sign Up Clicked").promise;
event is the final event as sent. code and message come from the server response or from the SDK:
code | message | Meaning | What the SDK does |
|---|---|---|---|
200 | Event tracked successfully | Amplitude accepted the event. | Removes the event from the queue. |
400 | Server error text, for example Invalid API key: ... | Amplitude rejected the event or the whole request. | Drops events the server marked invalid, and retries the rest. |
400 | Event rejected due to missing API key | init() received an empty API key. | Drops the event. |
413 | Server error text, for example Payload too large | The request was too large. | Halves flushQueueSize and retries. Drops a single event that's still too large. |
500 | Event rejected due to exceeded retry count | The upload failed flushMaxRetries (5) times. Network errors, 429 throttling, and server errors end here. | Drops the event. |
0 | Event skipped due to optOut config | Opt-out is on. | Never sends the event. |
0 | Event skipped due to empty event type | The event name is empty. | Never sends the event. |
0 | Empty | A before or enrichment plugin returned null. | Never sends the event. The SDK logs an error. |
A successful response body looks like this:
{
"code": 200,
"events_ingested": 1,
"payload_size_bytes": 412,
"server_upload_time": 1793919426800
}
A 200 response means Amplitude's ingestion API accepted the event. Amplitude can still exclude an accepted event from analysis later, for example when a block filter matches it.
Manual flushes don't retry
amplitude.flush() sends every queued event once and doesn't retry failures. Each event's promise resolves with the response status and body as the message, for example invalid: {"error": "Invalid API key: ..."}. Scheduled uploads retry and use the messages in the table.
Find the event in Amplitude
- Open User Lookup and search for the event's
user_id, or itsdevice_idif the user is anonymous. - Select the Activity tab and turn on Live event updates.
- Open the event and compare its properties with the
Final event before destinationslog line.
For events from many users, use the Ingestion Debugger to review accepted and rejected requests for the project. Amplitude's chart queries can take a few minutes longer to include new events than User Lookup does.
이 내용이 도움이 되었나요?
For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.