theonedev/onedev · error · ExplicitException

Unknown collection element class (bean: X, property: Y)

Error message

Unknown collection element class (bean: X, property: Y)

What it means

BuildSpecSchema generates a JSON schema from build spec bean classes. When a property returns a Collection, the code uses reflection to determine the collection's element class; if the generic return type carries no resolvable element type, it throws this ExplicitException because the schema cannot be built.

Solutions

  1. Add an explicit generic type parameter to the collection getter, e.g. List<String> instead of raw List.
  2. If the element type is a custom class, ensure the getter's generic return type is concrete (not a type variable).
  3. If writing a third-party spec class, follow OneDev's build spec conventions where all collection properties are strongly typed.

Example fix

// before
public List getSteps() { return steps; }
// after
public List<Step> getSteps() { return steps; }
Defensive patterns

Strategy: type-guard

Validate before calling

for (Method m : beanClass.getMethods()) {
    Type t = m.getGenericReturnType();
    if (Collection.class.isAssignableFrom(m.getReturnType())
            && !(t instanceof ParameterizedType))
        throw new IllegalStateException("Collection property needs generic type: " + m);
}

Type guard

function isTypedCollection(m) {
  return Collection.class.isAssignableFrom(m.getReturnType())
      && m.getGenericReturnType() instanceof ParameterizedType;
}

Try / catch

try {
    schema = BuildSpecSchema.generate(specClass);
} catch (ExplicitException e) {
    if (e.getMessage().startsWith("Unknown collection element class")) {
        // fix the offending raw collection getter named in the message
    }
}

Prevention

When it happens

Trigger: Processing a bean property whose getter returns a Collection (List/Set) with an unparameterized or non-resolvable generic type, so ReflectionUtils.getCollectionElementClass returns null and processProperty throws.

Common situations: Custom build spec property classes defining raw List or Set fields without a type parameter, or using wildcards/generics the reflection helper cannot resolve.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/1a0d53f96db293d2. Report an issue: GitHub.

Appendix: source

Thrown at server-core/src/main/java/io/onedev/server/ai/BuildSpecSchema.java:101

                        var grammar = IOUtils.toString(grammarStream, StandardCharsets.UTF_8);
                        descriptionSections.add("NOTE: If set, the value should conform with below ANTLR v4 grammar:\n\n" + grammar);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            } finally {
                IOUtils.closeQuietly(grammarStream);
            }
        }

        if (descriptionSections.size() != 0)
            currentNode.put("description", StringUtils.join(descriptionSections, "\n\n"));

        Class<?> elementClass = null;
        if (Collection.class.isAssignableFrom(returnType)) {
            elementClass = ReflectionUtils.getCollectionElementClass(property.getPropertyGetter().getGenericReturnType());
            if (elementClass == null)
                throw new ExplicitException("Unknown collection element class (bean: " + property.getBeanClass() + ", property: " + property.getPropertyName() + ")");
            processCollectionProperty(currentNode, elementClass);
        } else {
            processType(currentNode, returnType);
        }

        Object defaultValue;
        var value = property.getPropertyValue(bean);
        if (value instanceof Integer) {
            var intValue = (Integer) value;
            if (intValue == 0)
                defaultValue = null;
            else
                defaultValue = intValue;
        } else if (value instanceof Long) {
            var longValue = (Long) value;
            if (longValue == 0)
                defaultValue = null;
            else

View on GitHub (pinned to d44925c47c)