Diagnose Test Failures

VerifiedCaution

Systematic workflow to diagnose and fix test failures in Morphia, including analysis of generated bytecode.

Sby Skills Guide Bot
TestingAdvanced
10/20/2026
Claude Code
#test-failure#java#maven#bytecode#morphia

Recommended for

Automated summary

Provides a systematic procedure for diagnosing and fixing a failing Morphia test by inspecting source code, generated bytecode, and Critter's AOT/runtime paths.

Strengths

  • Six-step workflow from targeted test execution through post-fix verification.
  • Precise Maven commands with flags to skip dokka and avoid misapplied test filters.
  • Explains how to connect failures to Critter bytecode generators and distinguish AOT from runtime paths.
  • Includes concrete inspection techniques: javap, generated sources, and source jars in ~/.m2.

Limitations

  • Highly specific to the Morphia project and its Critter architecture; not directly reusable elsewhere.
  • Assumes solid knowledge of Java, Maven, and bytecode to interpret stack traces.
  • Allowed tools are limited to mvnw, javap, unzip, and grep; fixing files may require tools outside the skill.
When to use it

Use when a Morphia test fails and the likely cause lies in generated code or Critter's AOT/runtime path.

When not to use it

Avoid for diagnosing test failures in other Java projects or issues unrelated to Morphia bytecode generation.

Security analysis

Caution
Quality score85/100

The skill is a narrowly scoped debugging workflow for the Morphia project. All four declared tools are used for legitimate, read-mostly purposes: running tests/builds, decompiling with `javap`, inspecting jars with `unzip -p`, and searching with `grep`. There are no destructive commands (no `rm -rf`, no force pushes), no network exfiltration, no obfuscated/base64 payloads, no attempts to disable safety controls, and no instruction-override attempts in the content. The only reason it is not 'safe' is that it drives real Maven builds, which compile and execute arbitrary project/plugin code under the user's account — standard for a build-focused skill, but a genuine execution surface.

Findings
  • •Runs Maven build lifecycle (`./mvnw test`, `./mvnw install`), which executes build plugins and project code with the agent's privileges
  • •Invokes `unzip -p` against jars in `~/.m2/repository/`, i.e. reads local artifact files (read-only, no exfiltration)
  • •Bash(./mvnw:*) is a wildcard grant over the Maven wrapper, which executes project build plugins and can download/run arbitrary code from the local project and remote repositories (network access implied).
  • •Instructions direct decompiling/inspecting classes under ~/.m2/repository with unzip -p (read-only, but touches the shared local repository and can surface jar contents).
  • •No destructive, obfuscated, or exfiltrating instructions were found; the skill text contains no prompt-injection or role-manipulation attempts.
How this analysis is made

Examples

Failing Morphia core test
A test in morphia-core is failing with a NoSuchMethodError on a generated __read accessor. Run the failing test with Maven, inspect the generated classes in target/test-classes, determine whether the AOT or runtime Critter path produced the bad accessor, fix it minimally, and re-run the test.
Nestmate accessor failure
Diagnose this Morphia test failure: the runtime Critter path throws an IllegalAccessError from a hidden nestmate accessor. Trace the responsible generator, compare it with a working generator for the same pattern, apply a minimal fix, and verify the test class.
Rebuild dependencies first
The failing Morphia test depends on a recently changed module. Rebuild the necessary modules without tests and with deploy/invoker skips, run the single failing test, trace the root cause through source and generated bytecode, then verify the fix.

name: diagnose description: Use when a test fails and you need to diagnose the root cause. Run the test, read errors, trace through generated and source code, fix, and verify. allowed-tools: Bash(./mvnw:), Bash(javap:), Bash(unzip:), Bash(grep:)

Diagnose Test Failure

Overview

Systematic diagnosis of test failures in the Morphia project. Run the test, read the full error, trace the root cause through source and generated code, fix minimally, and verify.

Workflow

  1. Run the failing test

    ./mvnw test -pl :module-name -Dtest="ClassName#methodName" -Ddeploy.skip=true
    
    • Use -Ddeploy.skip=true to skip dokka
    • Do NOT use -am with -Dtest= (applies test filter to all modules)
    • If dependencies need rebuilding first:
      ./mvnw install -pl :dep1,:dep2 -DskipTests -Ddeploy.skip=true -Dinvoker.skip=true
      
  2. Read the full error - stack trace, error message, line numbers. Don't skip anything.

  3. Find relevant code - Read both source AND generated code:

    • Source: src/main/, src/test/
    • Generated bytecode: target/test-classes/ - use javap -c -p ClassName to decompile
    • Generated sources: target/generated-sources/
    • Interfaces from dependencies: check ~/.m2/repository/ source jars with unzip -p
  4. Trace the root cause - Follow the error backward:

    • What method is missing/wrong?
    • What generated it? All critter bytecode comes from the Class-File API generators in core/src/main/java/dev/morphia/critter/parser/generator/ (see "Critter Code Generation" below).
    • Was it the AOT path (critter-maven, woven __read*/__write* methods) or the runtime path (hidden nestmate accessors)?
    • What does the interface/superclass require?
    • Compare with a working generator for the same pattern (e.g. how PropertyAccessorGenerator boxes primitives)
  5. Fix minimally - One change at a time. Rebuild affected modules:

    ./mvnw install -pl :morphia-core,:critter-maven -DskipTests -Ddeploy.skip=true -Dinvoker.skip=true
    
  6. Verify - Re-run the failing test AND related tests in the same class.

Critter Code Generation

Critter generates bytecode with the Class-File API (java.lang.classfile), using the jdk-classfile-backport library (package io.smallrye.classfile) so it runs on Java 17. There is no Gizmo or ASM in the generators. Everything is in core/src/main/java/dev/morphia/critter/parser/generator/, and CritterGenerator is the entry point.

| Generator | Produces | |-----------|----------| | AddFieldAccessorMethods / AddMethodAccessorMethods | AOT only: rewrites the entity class to add synthetic __readXxx/__writeXxx methods | | PropertyAccessorGenerator | AOT only: a PropertyAccessor that calls the woven __read*/__write* methods | | NestmateAccessorGenerator | Runtime only: a PropertyAccessor using direct getfield/putfield/invokevirtual, defined as a hidden nestmate (Lookup.defineHiddenClass(..., NESTMATE)) and registered in NestmateAccessorRegistry | | PropertyModelGenerator | A CritterPropertyModel per property | | EntityModelGenerator | The CritterEntityModel for the entity | | VarHandleAccessorGenerator | A VarHandle/MethodHandle-based accessor; not used by the main pipeline right now (only TestVarHandleAccessor) |

The two paths:

  • AOT: critter-maven (generate-models / generate-test-models) calls CritterGenerator.generate(type, loader, false). It writes the generated models and the woven entity classes to disk under __morphia/<entity>/.
  • Runtime: when no pre-generated model is on the classpath, CritterMapper calls generate(type, loader, true). That defines classes in a CritterClassLoader plus hidden nestmates. If it fails, it logs once and falls back to a reflective EntityModel. Hidden nestmates need full privilege access, so an entity outside Morphia's module (on the classpath: loaded by a different class loader) fails with a NestmateAccessException; CritterMapper logs a specific "can't access ... isn't in Morphia's module" warning for it. That's expected for those layouts, and the fix is critter-maven AOT models, not a generator change.

PropertyFinder rejects some entities for AOT with UnsupportedOperationException("AOT skip: ..."): inherited fields the entity can't reach and whose declaring class can't be rewritten (e.g. a private field in a library superclass), fields shadowing a superclass field, array-typed fields, and entities whose @Id is on a getter. Entities with no @Id at all (e.g., embedded types) are generated normally. A final field's __writeXxx sets it through a cached java.lang.reflect.Field, since putfield may only write a final field from a constructor. An inherited field from a superclass in the same output directory or jar gets its __readXxx/__writeXxx methods woven into that superclass, which the entity inherits. The rejected entities use runtime generation instead, so an "AOT skip" warning in the critter-maven output is expected for them and is not an error.

Common Morphia/Critter Issues

| Error | Likely Cause | |-------|-------------| | AbstractMethodError | A generated class is missing an interface method. Generators emit methods with explicit erased descriptors (e.g. get(Ljava/lang/Object;)Ljava/lang/Object;), and nothing adds bridge methods for you | | VerifyError / Bad type on operand stack | Usually a primitive/reference mismatch: a primitive was used where an Object was expected, or the reverse, without boxing or unboxing | | ClassCastException in a generated accessor | checkcast to the wrong owner (for an inherited field, the declaring class vs the entity class) or to the wrong wrapper type | | IllegalAccessError from a nestmate accessor | The hidden class was defined against the wrong lookup class. It must be privateLookupIn the field's declaring class | | NoSuchMethodError __read*/__write* | The AOT accessor ran against an unwoven entity class, e.g. the woven class from generated-classes/critter is not ahead of the original on the classpath | | ClassNotFoundException __morphia.* | Critter code generation didn't run or class not registered |

Key Gotchas

  • The Class-File API is low-level: no automatic boxing, casting, or bridge methods. Emit each step explicitly.
  • Boxing: invokestatic Wrapper.valueOf(prim)Wrapper. Unboxing: checkcast Wrapper, then invokevirtual Wrapper.xxxValue(). Helpers live in GenerationUtils (PRIMITIVE_TO_WRAPPER, primitiveUnboxMethod, primitiveClassDesc).
  • checkcast only accepts reference types. To get a primitive from an Object, cast to the wrapper and then unbox.
  • Keep non-public types out of generated descriptors. AOT accessors use Object in __read*/__write* signatures for reference types, and the cast happens inside the entity.
  • Inspect generated classes with javap -c -p. AOT output is under target/generated-classes/critter (main) or target/test-classes/**/__morphia/ (tests). Runtime-generated classes are in memory only.

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/diagnose-test-failures-mvda86uq' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/diagnose-test-failures-mvda86uq/download' --output '.skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md.part' && mv '.skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md.part' '.skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Tools declared by the author (check availability in your agent): Bash(./mvnw:*), Bash(javap:*), Bash(unzip:*), Bash(grep:*)

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md' && test -s '.skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/diagnose' && cp -R '.skill-downloads/diagnose-test-failures-mvda86uq' '.claude/skills/diagnose'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/diagnose-test-failures-mvda86uq'

openskills read 'diagnose-test-failures-mvda86uq'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/diagnose-test-failures-mvda86uq/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills