{"record":{"id":"55403369408c19a5","repo":"theonedev/onedev","slug":"base-resource-mapper-should-be-used","errorCode":null,"errorMessage":"Base resource mapper should be used","messagePattern":"Base resource mapper should be used","errorType":"exception","errorClass":"ExplicitException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/web/WebApplication.java","lineNumber":276,"sourceCode":"\t\tsetRequestCycleProvider(context -> new RequestCycle(context) {\n\n\t\t\t@Override\n\t\t\tprotected UrlRenderer newUrlRenderer() {\n\t\t\t\treturn new AbsoluteUrlRenderer(getRequest());\n\t\t\t}\n\t\t\t\n\t\t});\n\t\t\n\t\tmount(new BaseUrlMapper(this));\n\t\t\n\t\tfor (WebApplicationConfigurator configurator: applicationConfigurators)\n\t\t\tconfigurator.configure(this);\n\t}\n\n\t@Override\n\tpublic void mount(IRequestMapper mapper) {\n\t\tif (mapper instanceof ResourceMapper && !(mapper instanceof BaseResourceMapper))\n\t\t\tthrow new ExplicitException(\"Base resource mapper should be used\");\n\t\tsuper.mount(mapper);\n\t}\n\n\t@Override\n\tpublic final IProvider<IExceptionMapper> getExceptionMapperProvider() {\n\t\treturn new IProvider<>() {\n\n\t\t\t@Override\n\t\t\tpublic IExceptionMapper get() {\n\t\t\t\treturn new DefaultExceptionMapper() {\n\t\t\t\t\t\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected IRequestHandler mapExpectedExceptions(Exception e, Application application) {\n\t\t\t\t\t\tRequestCycle requestCycle = RequestCycle.get();\n\t\t\t\t\t\tboolean isAjax = ((WebRequest) requestCycle.getRequest()).isAjax();\n\t\t\t\t\t\tif (isAjax && (e instanceof ListenerInvocationNotAllowedException || e instanceof ComponentNotFoundException || e instanceof InvalidBehaviorIdException))\n\t\t\t\t\t\t\treturn EmptyAjaxRequestHandler.getInstance();\n","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/web/WebApplication.java#L258-L294","documentation":"WebApplication.mount() overrides Wicket's mount to require that any IRequestMapper of type ResourceMapper also extends OneDev's BaseResourceMapper. BaseResourceMapper adds OneDev-specific handling (canonical URL generation etc.), so a raw Wicket ResourceMapper would break resource URL handling. A plain ResourceMapper passed to mount() triggers this ExplicitException.","triggerScenarios":"Calling application.mount(new ResourceMapper(...)) (or any ResourceMapper subclass that is not a BaseResourceMapper) during application/plugin initialization.","commonSituations":"Plugin authors following generic Wicket tutorials for mounting resource mappers; copying mapper code from a non-OneDev Wicket project into an OneDev initContributor.","solutions":["Use OneDev's BaseResourceMapper (or a subclass) instead of a raw ResourceMapper when mounting.","If you need custom mapper behavior, extend BaseResourceMapper and override only what you need.","Check which init contributor mounts the mapper and replace it with the OneDev base class."],"exampleFix":"// before\napp.mount(new ResourceMapper(\"/assets\", new MyResourceReference()));\n\n// after\napp.mount(new BaseResourceMapper(\"/assets\", new MyResourceReference()));","handlingStrategy":"validation","validationCode":"if (mapper instanceof ResourceMapper && !(mapper instanceof BaseResourceMapper)) {\n    throw new IllegalStateException(\"Use BaseResourceMapper instead of raw ResourceMapper\");\n}\napplication.mount(mapper);","typeGuard":"static boolean isMountable(IRequestMapper m) {\n    return !(m instanceof ResourceMapper) || m instanceof BaseResourceMapper;\n}","tryCatchPattern":null,"preventionTips":["Use BaseResourceMapper for all resource mapper mounting in OneDev plugins.","Do not copy mapper mounting code from generic Wicket tutorials verbatim.","Smoke-test plugin init locally with ./dev.sh run to catch mount failures early."],"tags":["wicket","resource-mapper","framework-convention"],"backgroundTag":"invalid-argument-value","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}