Error: Runtime exited with error: signal: killed

Short answer

Your function used more memory than it was allocated, and the Linux OOM killer terminated the process with SIGKILL. There is no stack trace and no exception because the process was not asked to stop — it was shot, and a killed process cannot log its own death.

What it means

Every Lambda invocation runs inside a Linux container with a hard memory limit set by your MemorySize configuration. That limit is enforced by the kernel's control groups, not by the runtime, and the kernel's enforcement mechanism is blunt: when a process in the cgroup tries to allocate past the limit, the OOM killer sends it SIGKILL.

SIGKILL is the one signal a process cannot catch, block, or handle. There is no shutdown hook, no finally block, no exception handler, and no opportunity to flush a log line. The process is removed from the scheduler between one instruction and the next. This is exactly why the failure is so disorienting the first time you meet it — every other Lambda failure leaves a stack trace pointing at a line of your code, and this one leaves a gap where your logs used to be.

What Lambda writes afterwards, Runtime exited with error: signal: killed, is the supervisor's observation, not your function's report. The supervisor noticed the runtime process was gone and recorded how it went. It has no idea what your code was doing at the time, which is why the message contains nothing useful about your program.

The useful information is in two places instead. The REPORT line's Max Memory Used equals Memory Size exactly — the allocation was still climbing when the process died, so the high-water mark is the ceiling. And your own last log line marks how far the work got before the allocation that crossed the line. Those two facts together tell you both that it was memory and where the memory went, which is why reading them next to each other — rather than hunting for a REPORT line among thousands, several screens away from the log lines it belongs to — is the difference between a two-minute diagnosis and an afternoon.

Counts

CloudWatch’s Errors metric: The error escaped your handler, so Lambda reports the invocation as failed and CloudWatch’s Errors metric counts it. LogStitch classified the invocation above as uncaught.

What it looks like in CloudWatch

This is the shape the failure arrives in: the lines of one invocation scattered among everything else the log group received at the same moment.

CloudWatch Logs Before

6 raw lines, in the order CloudWatch delivered them.

2026-08-29T04:12:08.114Z START RequestId: 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 Version: $LATEST
2026-08-29T04:12:08.118Z 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 INFO Export requested range=2026-07-01..2026-07-31
2026-08-29T04:12:08.402Z 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 INFO Fetching ledger objects prefix=exports/2026-07
2026-08-29T04:12:09.885Z 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 INFO Loaded 1 of 3 objects bytes=184320114
2026-08-29T04:12:11.204Z RequestId: 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 Error: Runtime exited with error: signal: killed
2026-08-29T04:12:11.204Z REPORT RequestId: 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 Duration: 3090.44 ms Billed Duration: 3091 ms Memory Size: 512 MB Max Memory Used: 512 MB

LogStitch After

The same lines, grouped into the invocation they belong to.

ErrorMemory ceiling3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01dur 3.09sbilled 3.09smem 512/512MBlogs 6
  1. 04:12:08.114PLAT
    START RequestId: 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 Version: $LATEST
  2. 04:12:08.118INFO
    INFO	Export requested range=2026-07-01..2026-07-31
  3. 04:12:08.402INFO
    INFO	Fetching ledger objects prefix=exports/2026-07
  4. 04:12:09.885INFO
    INFO	Loaded 1 of 3 objects bytes=184320114
  5. 04:12:11.204
    RequestId: 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01 Error: Runtime exited with error: signal: killed
  6. 04:12:11.204PLAT
    REPORT RequestId: 3f7a91cc-4f10-4c11-8f0b-1b2d3e4f5a01	Duration: 3090.44 ms	Billed Duration: 3091 ms	Memory Size: 512 MB	Max Memory Used: 512 MB
Error
Status
3.09s
Duration
3.09s
Billed
512/512MB
Memory
0%
Headroom
No
Cold start
RuntimeExit
Error type

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.

How to confirm it from the logs

The decisive evidence is on the REPORT line, not the error line. Compare Max Memory Used against Memory Size: for an OOM kill they are equal, because the process was killed the instant it crossed the ceiling. That equality is the confirmation — the signal: killed text alone can also mean the sandbox was reclaimed for another reason.

The second tell is the absence of anything. No stack trace, no [ERROR] line, no exception name. Your own logs simply stop, mid-work, at whatever the last thing you wrote was. If you have a stack trace, this is not an OOM kill.

Causes, most likely first

1

The function loads a whole payload into memory at once

How to confirm

Look at the last line before the logs stop, and at what your code does next. Reading an entire S3 object with getObject().Body.transformToString(), parsing a large JSON document, or collecting a full query result into an array all hold the complete payload in memory. If the size of that payload tracks the size of the input, memory use is proportional to input and the ceiling is one large record away.

2

The memory allocation is simply too low for the workload

How to confirm

Check Max Memory Used across several successful invocations of the same function. If the healthy ones already sit above about 85% of Memory Size, there is no headroom and any variation in input tips it over. A function that normally reports 241/256 MB is not comfortable — it is one busy request from being killed.

3

Memory accumulates across invocations in a warm container

How to confirm

Compare Max Memory Used on a cold start against a container that has served many requests. Lambda reuses execution environments, so anything at module scope — a cache that never evicts, an array appended to on every call, an event listener registered per invocation — survives between invocations and grows. Rising memory across consecutive requests on the same container is the signature.

4

A dependency buffers far more than you asked it to

How to confirm

Check whether a library sits between you and the data. Image processing, PDF generation, spreadsheet parsing, and some database drivers materialise the whole working set regardless of how you feed them. If your own code looks streaming but memory still spikes, the buffering is happening a layer down.

Fixes

Fix 1

Raise the memory allocation — and get more CPU with it

This is the fastest fix and often the correct one. Lambda scales CPU with memory, so a function given more memory frequently finishes faster as well, which can leave the bill flat or lower even though the per-millisecond rate went up. Set the value from evidence: take the highest Max Memory Used you have observed and leave roughly 30% headroom above it.

yaml# AWS SAM
Resources:
  ReportFunction:
    Type: AWS::Serverless::Function
    Properties:
      MemorySize: 1024   # was 512; observed peak was 512/512
Fix 2

Stream instead of buffering

If memory use tracks input size, no allocation is safe for long. Process the data as it arrives so peak memory depends on your chunk size rather than on the payload.

jsimport { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { createInterface } from "node:readline";

const s3 = new S3Client({});

export const handler = async (event) => {
  const object = await s3.send(new GetObjectCommand({
    Bucket: event.bucket,
    Key: event.key
  }));

  // One line in memory at a time, not the whole object.
  const lines = createInterface({ input: object.Body });
  let count = 0;
  for await (const line of lines) {
    await handleRow(JSON.parse(line));
    count += 1;
  }
  return { count };
};
Fix 3

Bound anything that lives at module scope

Module-scope state survives between invocations by design — that is what makes connection reuse work. It becomes a leak when it grows without limit. Give every cache a maximum size, and register listeners once rather than per invocation.

js// Wrong: grows forever across every invocation this container serves.
const cache = new Map();

// Right: bounded, so a warm container has a memory ceiling of its own.
const MAX = 500;
const cache = new Map();
function remember(key, value) {
  if (cache.size >= MAX) cache.delete(cache.keys().next().value);
  cache.set(key, value);
}

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.

RequestId: <id> Error: Runtime exited with error: signal: killedRuntime exited with error: signal: killed Runtime.ExitErrorlambda killed no error messagelambda function dies silently

Errors that show up alongside this one, or that people mistake for it.

References

LogStitch finds this automatically, across every invocation in your account.

Paste a log excerpt into the free web stitcher and see it grouped, classified, and measured in your browser — nothing is uploaded. Or run the Mac app against your own AWS profiles and get the same view over every function you own.