{"record":{"id":"396401e092f5ef87","repo":"halo-dev/halo","slug":"unexpected-apiversion-string","errorCode":null,"errorMessage":"Unexpected APIVersion string: {}","messagePattern":"Unexpected APIVersion string: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/run/halo/app/extension/GroupVersion.java","lineNumber":34,"sourceCode":"    public String toString() {\n        return StringUtils.hasText(group) ? group + \"/\" + version : version;\n    }\n\n    /**\n     * Parses APIVersion into GroupVersion record.\n     *\n     * @param apiVersion must not be blank. 1. If the given apiVersion does not contain any \"/\", we treat the group is\n     *     empty. 2. If the given apiVersion contains more than 1 \"/\", we will throw an IllegalArgumentException.\n     * @return record contains group and version.\n     */\n    public static GroupVersion parseAPIVersion(String apiVersion) {\n        Assert.hasText(apiVersion, \"API version must not be blank\");\n\n        var groupVersion = apiVersion.split(\"/\");\n        return switch (groupVersion.length) {\n            case 1 -> new GroupVersion(\"\", apiVersion);\n            case 2 -> new GroupVersion(groupVersion[0], groupVersion[1]);\n            default -> throw new IllegalArgumentException(\"Unexpected APIVersion string: \" + apiVersion);\n        };\n    }\n}\n","sourceCodeStart":16,"sourceCodeEnd":38,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/api/src/main/java/run/halo/app/extension/GroupVersion.java#L16-L38","documentation":"Thrown by GroupVersion.parseAPIVersion when the apiVersion string, after splitting on '/', yields more than 2 segments. Halo encodes an Extension's apiVersion as either 'version' (empty group) or 'group/version'; a string with 2+ slashes (e.g. 'a/b/c') has no valid group/version mapping and is rejected. The blank-input case is caught earlier by Assert.hasText.","triggerScenarios":"GroupVersion.parseAPIVersion(\"content.halo.run/v1alpha1/extra\"), parseAPIVersion(\"a/b/c\"), or any apiVersion containing more than one '/'.","commonSituations":"A client concatenates group + '/' + version + '/' + subresource; an extension GVK string mis-parsed to include a trailing slash segment; a URL path fragment mistaken for apiVersion; copied 'group/version/kind' instead of 'group/version'.","solutions":["Pass apiVersion strictly as 'group/version' (e.g. 'content.halo.run/v1alpha1') or just 'version' for core resources.","Strip anything after the second segment before calling parseAPIVersion.","Validate with `apiVersion.chars().filter(c -> c == '/').count() <= 1` before parsing."],"exampleFix":"// before\nGroupVersion.parseAPIVersion(\"content.halo.run/v1alpha1/posts\");\n\n// after\nGroupVersion.parseAPIVersion(\"content.halo.run/v1alpha1\");","handlingStrategy":"validation","validationCode":"long slashes = apiVersion.chars().filter(c -> c == '/').count();\nif (slashes > 1) {\n    throw new IllegalArgumentException(\"apiVersion must be 'group/version' or 'version': \" + apiVersion);\n}\nGroupVersion gv = GroupVersion.parseAPIVersion(apiVersion);","typeGuard":"boolean validApiVersion = apiVersion != null && !apiVersion.isBlank()\n    && apiVersion.chars().filter(c -> c == '/').count() <= 1;","tryCatchPattern":"try {\n    GroupVersion gv = GroupVersion.parseAPIVersion(apiVersion);\n} catch (IllegalArgumentException e) {\n    // log and reject the malformed resource\n}","preventionTips":["Construct apiVersion from GroupVersion.toString() rather than string concatenation.","Reject resources whose apiVersion has >1 '/' at the deserialization boundary.","Never append a kind or subresource to the apiVersion string."],"tags":["java","extension","group-version","api","validation"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}