elastic/elasticsearch · error · RuntimeException

TransportVersion.fromName must be called with a non-empty St

Error message

TransportVersion.fromName must be called with a non-empty String literal. See {}.

What it means

RuntimeException from CollectTransportVersionReferencesTask's ASM bytecode visitor. It scans compiled classes for calls to TransportVersion.fromName(...) and requires the argument to be a non-empty LDC string literal (a compile-time constant). Any other shape (variable, method call result, computed value, or empty literal) is rejected because the static analysis cannot record a stable transport-version name.

Source

Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/transport/CollectTransportVersionReferencesTask.java:121

                    @Override
                    public void visitLabel(Label label) {
                        // asm uses many debug labels that we do not want to consider
                        // so we ignore labels so they do not become part of the instructions list
                    }

                    @Override
                    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
                        if (owner.equals(TRANSPORT_VERSION_SET_CLASS) && name.equals(TRANSPORT_VERSION_SET_METHOD_NAME)) {
                            var abstractInstruction = this.instructions.getLast();
                            String location = classname + " line " + lineNumber;
                            if (abstractInstruction instanceof LdcInsnNode ldcInsnNode
                                && ldcInsnNode.cst instanceof String tvName
                                && tvName.isEmpty() == false) {
                                results.add(new TransportVersionReference(tvName, location));
                            } else {
                                // The instruction is not a LDC with a String constant (or an empty String), which is not allowed.
                                throw new RuntimeException(
                                    "TransportVersion.fromName must be called with a non-empty String literal. " + "See " + location + "."
                                );
                            }
                        }
                        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
                    }
                };
            }
        };
        ClassReader classReader = new ClassReader(classBytes);
        classReader.accept(classVisitor, 0);
    }

    private static String classname(String filename) {
        return filename.substring(0, filename.length() - CLASS_EXTENSION.length()).replaceAll("[/\\\\]", ".");
    }
}

View on GitHub (pinned to db6a809a66)

Solutions

  1. Locate the call at the reported 'classname line N' and replace the argument with a non-empty string literal.
  2. If a shared constant is needed, make it a compile-time constant (static final String literal) so javac inlines it as LDC.
  3. Never pass dynamically-computed strings to TransportVersion.fromName.

Example fix

// before
TransportVersion v = TransportVersion.fromName(versionNameFromConfig);

// after
TransportVersion v = TransportVersion.fromName("my_feature_v1");
Defensive patterns

Strategy: validation

Validate before calling

// Static-analysis guard in source: only allow string literals
static final Set<String> ALLOWED = Set.of("feature_a_v1", "feature_b_v1");
void register(String name) {
    if (name == null || name.isEmpty() || !ALLOWED.contains(name)) {
        throw new IllegalArgumentException("TransportVersion name must be a known literal: " + name);
    }
    TransportVersion.fromName(name);
}

Type guard

// Compile-time-ish guard: prevent dynamic names at review time
static void assertLiteral(String name) {
    if (name == null || name.isEmpty()) throw new IllegalArgumentException("empty name");
}

Prevention

When it happens

Trigger: Source code calls TransportVersion.fromName(someVariable), TransportVersion.fromName(""), or passes a concatenated/computed string; ASM's last instruction before the call is not an LdcInsnNode with a String constant.

Common situations: Refactoring a fromName call to use a constant field or a computed value; copy-pasting a fromName placeholder with an empty string; using a non-final String field (not inlined to LDC).

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/128557a90317aa06. Report an issue: GitHub.