skip to content

An AWS Lambda function's configuration includes a runtime (for example python3.12) and a handler string (for example app.lambda_handler). What does each of those identify in your deployment package, and what does Lambda report if the handler string does not match what you shipped?

level: juniorimportance: should knowfreq 58%

answer

  1. configuration, not code
  2. file on the left, function on the right
  3. resolved from the archive root
  4. zip the contents, not the folder
  5. fails before your first log line

basics

~20 s

The runtime selects the language environment Lambda starts; the handler names the entry point inside the package — the file or module, then the function in it. A mismatch fails during initialization, before any of your code runs.

solid answer

~50 s

Those are two separate pieces of function configuration, not code. The **runtime** (`python3.12`, `nodejs20.x`, `java21`, or `provided.al2023` for a custom one) tells Lambda which language execution environment to start and which runtime bootstrap to run. The **handler** tells that bootstrap what to load: for Python and Node.js it is `file.function` — `app.lambda_handler` means "import the module `app` and call `lambda_handler`" — and for Java it is `package.Class::method`. Both are resolved relative to the root of the extracted deployment package, so if you zipped the project *folder* rather than its contents, the module sits one directory too deep and cannot be found. When resolution fails you get an initialization error before your first log line: `Runtime.ImportModuleError` when the module isn't there, `Runtime.HandlerNotFound` when the module loads but the named function isn't exported. Because the handler is configuration, you can point it at a different entry point without rebuilding the package.

go deeper

for a junior

Be able to read a handler string out loud: file or module on the left, function on the right, both relative to the top of the deployment package. Also know that an import error appears before any of your own logs.

for a middle

Explain that runtime and handler are configuration you can change without redeploying code, describe the per-language handler formats, and name the two initialization error types and what each one tells you about the package layout.

for a senior

Show how you diagnose a failing deployment quickly — inspect the archive listing, check where dependencies landed, distinguish an import failure from an application error — and treat runtime deprecation as a scheduled upgrade you own rather than a surprise.

for a principal

Own the packaging convention across a fleet: one build process that guarantees archive layout, a policy for which runtimes are approved and how their end-of-support dates are tracked, and a decision about whether teams ship many entry points from one artifact or one handler per function.

## Two settings, both configuration Every Lambda function carries a `Runtime` and a `Handler` in its configuration, alongside memory, timeout and the execution role. Neither lives in your code — they are properties of the function that you can change with an `UpdateFunctionConfiguration` call and no new upload at all. Understanding that they are just *pointers* explains most of the errors beginners hit. ## The runtime identifier The runtime identifier is a fixed string from AWS's published list: `python3.12`, `nodejs20.x`, `java21`, `dotnet8`, `ruby3.3`, and `provided.al2023` for a custom runtime. It selects the managed execution environment Lambda boots — the operating-system image, the language interpreter or JVM, and the *runtime bootstrap*, a small AWS-supplied program that talks to the Lambda Runtime API. That bootstrap is what actually polls for the next event, loads your handler, invokes it, and posts the response back. Runtimes are versioned and eventually deprecated; when a runtime reaches end of support, AWS stops applying security patches to it and eventually blocks updates and then invocations. Treating the runtime string as something you upgrade deliberately — rather than something set once at creation — is part of owning a function. If you pick `provided.al2023`, there is no language bootstrap: you ship an executable named `bootstrap` in the package, and *it* is responsible for calling the Runtime API in a loop. In that case the handler string is not parsed by AWS at all — it is simply handed to your process in the `_HANDLER` environment variable for you to interpret. ## The handler string The handler is a runtime-specific path to your entry point, resolved from the root of the extracted deployment package. - **Python** — `file.function`. `app.lambda_handler` means module `app` (that is, `app.py`, or a package `app/`) and a top-level function `lambda_handler(event, context)`. - **Node.js** — `file.exportedName`. `index.handler` means `index.js` / `index.mjs` exporting `handler`. - **Java** — `package.Class::method`, e.g. `com.example.App::handleRequest`. - **Go / custom runtimes on `provided.al2023`** — the string is conventional only; your `bootstrap` decides what it means. Nothing forces the names `handler` or `lambda_function`; those are just what the console scaffolding generates. Sub-directories are expressed with the runtime's own separator, so `src/api.handler` (Node) or `src.api.lambda_handler` (Python) are both valid when the code sits under `src/`. ```python # app.py at the root of the .zip -> handler = app.lambda_handler def lambda_handler(event, context): return {"statusCode": 200, "body": "ok"} ``` ## What happens when it doesn't match Handler resolution happens during the initialization of a fresh execution environment, *before* your module-level code runs — which is why a broken handler produces no application logs at all, only an error record. The two error types you will see are: - `Runtime.ImportModuleError` — the module or file named on the left of the dot could not be imported. Classic causes: the archive was created by zipping the containing folder (so every path gained a prefix), the file has the wrong extension, or a third-party dependency imported at module scope isn't in the package. - `Runtime.HandlerNotFound` — the module loaded fine but the function on the right of the dot isn't exported, is misspelled, or is defined inside a class or another function. Both are *initialization* failures. For a synchronous invoke the caller gets the error straight back; for an asynchronous invoke Lambda retries on its own schedule, so a bad deploy shows up as a burst of failures rather than one. ## The packaging mistakes behind most of these ```bash cd my-function && zip -r ../function.zip . # correct: contents at the archive root zip -r function.zip my-function/ # wrong: everything under my-function/ ``` A quick `unzip -l function.zip` before uploading answers the question definitively: the handler's file must appear at the top of that listing, not nested. The same rule applies to dependencies installed with `pip install -t .` or `npm install --production` — they belong beside your handler file, not in a parent directory. ## Container images are the same idea, expressed differently When a function is packaged as a container image, the image's `CMD` supplies the handler string and the base image supplies the runtime interface client, so there is no separate `Handler`/`Runtime` configuration to get wrong — but the same resolution happens inside the container, and the same two error types appear when the entry point isn't where the client expects it.

  • Can you change a function's handler without uploading new code, and when would you want to?
    Yes — the handler is function configuration, so an `UpdateFunctionConfiguration` call changes it while the package stays the same. It is useful when one package contains several entry points, for example a shared library shipped once with separate handlers for an HTTP path and a queue consumer, or when temporarily pointing at a diagnostic entry point.
  • Why does code you write outside the handler function behave differently from code inside it?
    Module-level code runs once when the execution environment is created, while the handler body runs on every invocation. That makes module scope the right place for expensive one-time setup such as building a client, and the wrong place for anything request-specific — a value computed there is reused by every later invocation on that environment.
  • Your Python function imports a third-party library and fails with Runtime.ImportModuleError naming that library, not your module. What's wrong?
    The dependency isn't in the package. Lambda's managed runtimes ship only the standard library plus a small set of preinstalled packages, so anything else must be vendored into the archive (for example `pip install -r requirements.txt -t .`) or supplied by a layer. The error surfaces at init because the import runs at module scope.

saying these in an interview costs you the question

  • Thinks the handler function must literally be named handler
  • Believes the runtime is chosen by a file inside the package
  • Zips the project folder so every path gains a prefix
  • Assumes managed runtimes include your third-party dependencies
  • Confuses the handler string with the function name or ARN

context