What it is
If you have switched a function's log format to JSON, the familiar REPORT line disappears and this arrives instead. It is the same information — same numbers, same request ID, same cold-start figure — wrapped in an envelope with a type discriminator and a nested record. Nothing has been lost; it has been made machine-readable, at the cost of being much harder to read by eye.
The reason this record matters more than the other three is maxMemoryUsedMB. Lambda publishes CloudWatch metrics for invocations, errors, duration, throttles and concurrency. It publishes nothing for memory. The high-water mark for an invocation exists in exactly one place — this record, or the text REPORT line it replaces — and nowhere else. That is why functions routinely sit at ninety-five percent utilisation for months without anything on a dashboard changing, and then start dying during a traffic peak.
status is the second thing worth knowing about. On a timeout it reads timeout, and that is a far more trustworthy signal than searching log text for the phrase "Task timed out" — the text may be absent, truncated, or interleaved with other executions, while this field is written by the platform itself. The same applies to errorType, which carries the runtime's own classification of a failure rather than whatever your code happened to print.
The awkwardness is purely one of reading. In the text format a REPORT is one line you can scan. In JSON it is an envelope that, pretty-printed by a log viewer, occupies ten to fifteen lines and pushes your own output off the screen. That is a real cost of enabling structured logging, and it is the reason a tool reading these should render them the way the text format did — one line, the numbers visible, the envelope available when you actually want it.
What it looks like in your logs
With Advanced Logging Controls set to JSON, this record arrives as an envelope. Here it is raw, and then read the way the text format would have shown it.
CloudWatch Logs Before
5 raw lines, in the order CloudWatch delivered them.
LogStitch After
The same lines, grouped into the invocation they belong to.
SuccessCold start · 284ms init8c1f4a20-7b93-4e05-a618-2d9c7e0b3f41
- 09:12:03.114PLAT
INIT_START Runtime Version: nodejs:22.v14
- 09:12:03.402PLAT
START RequestId: 8c1f4a20-7b93-4e05-a618-2d9c7e0b3f41 Version: $LATEST
- 09:12:03.410
Processing order ord_92ba7c
- 09:12:03.688PLAT
RUNTIME_DONE RequestId: 8c1f4a20-7b93-4e05-a618-2d9c7e0b3f41 Status: success Duration: 278.44 ms
- 09:12:03.691PLAT
REPORT RequestId: 8c1f4a20-7b93-4e05-a618-2d9c7e0b3f41 Duration: 278.44 ms Billed Duration: 279 ms Memory Size: 512 MB Max Memory Used: 94 MB Init Duration: 284.11 ms
The panel on the right is generated by running the excerpt on the left through the same parser that powers the free web stitcher — it is what the tool actually produces for this input, not an illustration of it.
What the record contains
Every field Lambda writes in this record, and what each one is telling you.
| Field | Type | Meaning |
|---|---|---|
| requestId | string | The invocation's request ID. Every other record for this execution carries the same value, and it is what groups them together. |
| status | string | The runtime's verdict — success, error, or timeout. On a timeout this is the most authoritative signal available, more trustworthy than scanning log text for "Task timed out". |
| errorType | string | Present on failure. Carries the runtime's classification, such as Runtime.ImportModuleError or Runtime.Unknown. |
| metrics.durationMs | number | Wall-clock time the handler ran, excluding initialisation. This is what you are optimising when you optimise the function. |
| metrics.billedDurationMs | number | What you actually pay for, rounded up to the millisecond. On a cold start it includes init time, so it can exceed durationMs. |
| metrics.memorySizeMB | number | The memory you configured for the function. |
| metrics.maxMemoryUsedMB | number | The high-water mark the runtime observed. Compare it against memorySizeMB — equality means the invocation touched its ceiling, and this figure exists nowhere else in CloudWatch. |
| metrics.initDurationMs | number | Present only on a cold start, measuring the initialisation phase. Its presence is the authoritative cold-start signal. |
When it appears
Once per invocation, always, as the last record for that request ID. It is emitted whether the invocation succeeded, failed, timed out, or was killed, which makes it the most reliable anchor in a log group: if a request ID has a platform.report, that execution reached the end of its lifecycle one way or another.
It appears in this JSON shape only when the function's log format is set to JSON via Advanced Logging Controls. With the default text format the same information arrives as a tab-separated REPORT RequestId: … Duration: … ms line. The fields are the same; only the encoding differs — which is worth knowing because most tooling, and most StackOverflow answers, assume the text form.
On a cold start the record also carries initDurationMs. Its presence is the authoritative cold-start signal — more reliable than inferring one from a preceding platform.initStart, and far more reliable than guessing from the first invocation on a log stream.
Also seen as
The same underlying failure, worded differently by a different runtime, SDK version, or logging layer. All of these land here — there is no separate page for each phrasing.
Related errors
Errors that show up alongside this one, or that people mistake for it.
References
- AWS Lambda Developer Guide — Configuring advanced logging controls
- AWS Lambda Developer Guide — Lambda Telemetry API Event schema reference
Stop reading JSON envelopes.
LogStitch renders every one of these records as a single readable line, in the invocation it belongs to — the same way the text format shows them, with the raw envelope one toggle away. Paste a log excerpt into the free web stitcher and see it, or run the Mac app against your own AWS profiles.