{"record":{"id":"25ba7508b80ea414","repo":"theonedev/onedev","slug":"property-type-is-reserved-class-x","errorCode":null,"errorMessage":"Property 'type' is reserved (class: X)","messagePattern":"Property 'type' is reserved \\(class: X\\)","errorType":"exception","errorClass":"ExplicitException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/BuildSpecSchema.java","lineNumber":221,"sourceCode":"                            lastValueBytes = SerializationUtils.serialize((Serializable) value);\n                            valueBytesMap.put(property.getPropertyName(), lastValueBytes);\n                        } else if (!Arrays.equals(lastValueBytes, SerializationUtils.serialize((Serializable) value))) {\n                            excludedProperties.add(property.getPropertyName());\n                        }\n                    }\n                }\n            }                        \n        }\n        if (bean == null) {\n            bean = newBean(beanClass);\n        }\n        \n        var dependents = new ArrayList<Pair<PropertyDescriptor, DependsOn[]>>();\n        for (var groupProperties: beanDescriptor.getProperties().values()) {\n            for (var property: groupProperties) {\n                if (!excludedProperties.contains(property.getPropertyName()) && processedProperties.add(property.getPropertyName())) {\n                    if (property.getPropertyName().equals(\"type\"))\n                        throw new ExplicitException(\"Property 'type' is reserved (class: \" + beanClass.getName() + \")\");\n                    var dependsOns = property.getPropertyGetter().getAnnotationsByType(DependsOn.class);\n                    if (dependsOns.length != 0) {\n                        dependents.add(new Pair<>(property, dependsOns));\n                    } else {\n                        if (property.isPropertyRequired())\n                            requiredNode.add(property.getPropertyName()); \n                        var propNode = new HashMap<String, Object>();\n                        propsNode.put(property.getPropertyName(), propNode);\n                        processProperty(propNode, bean, property);\n                    }\n                }\n            }\n        }\n        if (!dependents.isEmpty()) {\n            var allOfNode = new ArrayList<Map<String, Object>>();\n            currentNode.put(\"allOf\", allOfNode);\n            for (var dependent: dependents) {\n                var allOfItemNode = new HashMap<String, Object>();","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/BuildSpecSchema.java#L203-L239","documentation":"In generated JSON schemas, the 'type' property name is reserved for polymorphic type discrimination. processBean iterates a bean's properties and throws this ExplicitException if any property is literally named 'type', since it would clash with the discriminator.","triggerScenarios":"Defining a build spec bean class (processed via processType/processPolymorphic) that has a getter/property named 'type' which is not in the excludedProperties set, then generating the schema.","commonSituations":"Custom spec classes mirroring external YAML formats that use 'type' as a field name, or porting an existing config class into OneDev's build spec model without renaming the property.","solutions":["Rename the property to something else (e.g. 'executorType', 'kind') in the bean class.","If 'type' must appear in the output YAML, keep the Java property name different and add an @Property/description mapping as supported by the spec framework.","Exclude the property from schema processing if your version exposes an exclusion mechanism (excludedProperties) for it."],"exampleFix":"// before\nprivate String type;\npublic String getType() { return type; }\n// after\nprivate String executorType;\npublic String getExecutorType() { return executorType; }","handlingStrategy":"validation","validationCode":"for (PropertyDescriptor p : beanProperties) {\n    if (p.getPropertyName().equals(\"type\"))\n        throw new IllegalStateException(\"'type' is reserved: \" + beanClass.getName());\n}","typeGuard":null,"tryCatchPattern":"try {\n    schema = BuildSpecSchema.generate(specClass);\n} catch (ExplicitException e) {\n    if (e.getMessage().contains(\"Property 'type' is reserved\")) {\n        // rename the property in the class indicated by the message\n    }\n}","preventionTips":["Avoid 'type' as a property name in spec beans.","Reserve 'type' for the polymorphic discriminator only.","Add unit tests generating schemas for all custom bean classes."],"tags":["schema","naming","buildspec","reflection"],"backgroundTag":"schema-validation-failed","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}