What it means
Java resolves most classes when they are first needed rather than all at once at startup, and it resolves them by name against a classpath. Inside a Lambda execution environment that classpath is fixed: /var/task/, every jar under /var/task/lib/, and anything a layer places under /opt. Nothing from your build machine, your IDE's run configuration, or your local Maven repository is present.
ClassNotFoundException is thrown when something asked the classloader for a class by name and it was not found on that path. The "by name" part matters, and is the reason this error survives a successful compile: reflection, service loaders, dependency-injection frameworks and JSON deserializers all resolve classes from strings at runtime, so the compiler has nothing to check. Lambda itself does exactly this to load your handler, which is why a handler misconfiguration surfaces as a ClassNotFoundException naming your own class.
Because resolution is lazy, the timing of the failure is informative. A class needed to construct your handler fails during init, so every invocation dies the same way and none of your code runs. A class needed only on one branch — an error formatter, an optional integration — fails only when that branch is taken, which produces a deployment that appears healthy until a particular kind of request arrives. Reading the stack trace to see where the load was attempted separates those two immediately.
The distinction worth keeping straight is against its close relative, NoClassDefFoundError. ClassNotFoundException means the class was never found. A NoClassDefFoundError means it was found once, failed to initialise, and every subsequent attempt to use it fails too. They read almost identically in a log and point at completely different problems — one is a packaging fault, the other is usually an exception thrown in a static initialiser.
CloudWatch’s Errors metric: The error escaped your handler, so Lambda reports the invocation as failed and CloudWatch’s Errors metric counts it. The class cannot be loaded, so the invocation fails and CloudWatch counts it. LogStitch extracts the type ClassNotFoundException but classifies the failure as unclassified rather than uncaught, because Java code can legitimately catch this exception — the log line alone does not say whether anything did.
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
7 raw lines, in the order CloudWatch delivered them.
LogStitch After
The same lines, grouped into the invocation they belong to.
Errorb2c47e91-5a08-4f36-bd12-7e0a3c9f4d28
- 09:12:03.771PLAT
START RequestId: b2c47e91-5a08-4f36-bd12-7e0a3c9f4d28 Version: $LATEST
- 09:12:04.118
java.lang.ClassNotFoundException: com.example.orders.PricingRules
- 09:12:04.118
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
- 09:12:04.118
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) at com.example.orders.OrderHandler.handleRequest(OrderHandler.java:52)
2 lines - 09:12:04.402PLAT
END RequestId: b2c47e91-5a08-4f36-bd12-7e0a3c9f4d28
- 09:12:04.402PLAT
REPORT RequestId: b2c47e91-5a08-4f36-bd12-7e0a3c9f4d28 Duration: 631.09 ms Billed Duration: 632 ms Memory Size: 1024 MB Max Memory Used: 248 MB Status: error
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
Read the fully-qualified class name in the message and decide which of two cases you have. If it is your own handler class, Lambda could not load the entry point at all and the handler configuration or the artifact layout is wrong. If it is a third-party class — something under com.amazonaws, org.apache, com.fasterxml — a dependency did not make it into the jar.
The stack trace beneath tells you when it happened. A failure inside URLClassLoader.findClass during init means the class was needed to construct your handler. A failure deeper in your own call stack means it was needed lazily, part-way through a request — which is why some invocations can succeed while others fail on the same deployment.
Causes, most likely first
The dependency was not included in the deployment artifact
List the contents of the deployed jar or zip and look for the class's path. A plain jar built by mvn package contains only your own compiled classes — dependencies are not included unless the build is configured to include them, which is the single most common cause here.
The handler string does not match the class in the artifact
Compare the configured handler against the actual fully-qualified class name. Lambda expects package.ClassName::methodName, or just package.ClassName when the class implements RequestHandler. A typo, a missing package prefix, or a renamed class produces exactly this error naming your own class.
A shaded or relocated dependency moved the class
Check whether the build shades dependencies, and whether the missing class's package looks relocated. Shading rewrites package names inside the jar; anything that loads a class by string name — reflection, a service loader, a framework's configuration — will still ask for the original name and not find it.
The class is provided at compile time but not at runtime
Look at the dependency's scope in the build file. A Maven dependency with <scope>provided</scope> or a Gradle compileOnly is on the compile classpath and deliberately excluded from the artifact. That is correct for the Lambda runtime interface itself, and wrong for anything your code actually needs at runtime.
Two versions of the same library disagree about what exists
Print the dependency tree and look for the missing class's library appearing more than once. When two versions are present, whichever the classloader reaches first wins, and a class that exists only in the other version becomes unfindable at runtime while compiling perfectly.
Fixes
Build an artifact that actually contains your dependencies
Lambda needs either an uber-jar or a zip with lib/ inside it. The Maven Shade plugin produces the former; the AWS-recommended layout for larger functions is the latter, which unpacks faster on a cold start.
xml<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
</execution>
</executions>
</plugin>
Confirm the class is present before you deploy again
Checking the artifact takes a few seconds and settles the question of whether this is a build problem or a configuration one.
bash# Is the class actually in there?
unzip -l target/function.jar | grep -i 'MissingClass'
# And what does the handler string need to match?
unzip -p target/function.jar META-INF/MANIFEST.MF
Check the handler configuration against the real class name
When the missing class is your own, the artifact is usually fine and the handler string is not. It must be the fully-qualified name, package included.
yamlResources:
OrderFunction:
Type: AWS::Serverless::Function
Properties:
# package.Class::method — the package prefix is required
Handler: com.example.orders.OrderHandler::handleRequest
Runtime: java21
Resolve duplicate versions before assuming the class is absent
Where the dependency tree shows a library twice, pin it once explicitly. A class that exists in one version and not the other will otherwise resolve differently depending on classloader order, which makes the failure look intermittent.
bashmvn dependency:tree -Dverbose -Dincludes=com.fasterxml.jackson.core
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 — Deploy Java Lambda functions with .zip or JAR file archives
- AWS Lambda Developer Guide — Define Lambda function handler in Java
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.