# Lambda Using Deprecated Runtime

> Deprecation is decided by the discoverer's is_deprecated_runtime flag, not a hardcoded version list that goes stale, so a runtime AWS deprecates next quarter is caught without a rule change. Deprecated runtimes stop receiving security patches first, then lose the ability to create or update functions in 2 later enforcement phases.

Source: https://zop.dev/integrations/aws/recommendations/lambda-using-deprecated-runtime
Updated: 2026-08-19

---

## Deprecation is a countdown, not a label

AWS retires Lambda runtimes in phases. First the runtime stops receiving security patches, so
your function keeps running on an interpreter that will never see another CVE fix. Then AWS
blocks creating new functions on it, then blocks updates to existing ones, and eventually
invocations themselves can be disabled. A function on a deprecated runtime is therefore not
"working fine"; it is at a known point on a published schedule toward not working at all,
carrying unpatched-interpreter risk the whole way. That is why this finding is rated critical
despite costing nothing.

## The determination lives upstream, deliberately

An earlier implementation kept its own list of deprecated runtime strings, which is a design
that rots: every AWS deprecation announcement required a code change nobody remembered to
make. The current rule reads a per-function `is_deprecated_runtime` flag the discoverer
computes, plus the runtime string for display. If the flag is absent the rule abstains, and if
the runtime name is missing there is nothing meaningful to report, so it abstains then too.
One source of truth, no stale allow-list.

## Enumerate your runtime spread

```bash
aws lambda list-functions \
  --query 'Functions[].[FunctionName,Runtime]' --output table
```

Cross-check anything old-looking against the runtime deprecation table in the Lambda
documentation. python3.8, nodejs16.x, go1.x and their contemporaries are the usual finds.

## Migration is mostly testing, rarely rewriting

Within a language, runtime upgrades are usually mechanical: bump the runtime setting, run the
test suite, watch for removed stdlib behaviour and updated SDK defaults. The genuinely painful
cases are functions whose dependencies pin dead versions, and functions nobody owns, which
this finding tends to surface for the first time in years. Batch the mechanical upgrades,
schedule the painful ones, and treat any function with no owner and no traffic as a deletion
candidate instead of a migration.

## Keep the class from coming back

Runtime lifetimes are finite by design, so build the upgrade into cadence: pin runtimes in
IaC, subscribe to the deprecation schedule, and prefer container-image functions where you
control the base image lifecycle when a team genuinely cannot track managed-runtime timelines.
