{"record":{"id":"861546366d635eb9","repo":"theonedev/onedev","slug":"script-should-return-either-a-map-or-a-list","errorCode":null,"errorMessage":"Script should return either a Map or a List","messagePattern":"Script should return either a Map or a List","errorType":"exception","errorClass":"ExplicitException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/buildspecmodel/inputspec/choiceinput/choiceprovider/ScriptingChoices.java","lineNumber":57,"sourceCode":"\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic Map<String, String> getChoices(boolean allPossible) {\n\t\tMap<String, Object> variables = new HashMap<>();\n\t\tvariables.put(\"allPossible\", allPossible);\n\t\t\n\t\ttry {\n\t\t\tObject result = GroovyUtils.evalScriptByName(scriptName, variables);\n\t\t\tif (result instanceof Map) {\n\t\t\t\treturn (Map<String, String>) result;\n\t\t\t} else if (result instanceof List) {\n\t\t\t\tMap<String, String> choices = new HashMap<>();\n\t\t\t\tfor (String item: (List<String>)result)\n\t\t\t\t\tchoices.put(item, null);\n\t\t\t\treturn choices;\n\t\t\t} else {\n\t\t\t\tthrow new ExplicitException(\"Script should return either a Map or a List\");\n\t\t\t}\n\t\t} catch (RuntimeException e) {\n\t\t\tif (allPossible) {\n\t\t\t\tlogger.error(\"Error getting all possible choices\", e);\n\t\t\t\treturn new HashMap<>();\n\t\t\t} else {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n}\n","sourceCodeStart":39,"sourceCodeEnd":70,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/buildspecmodel/inputspec/choiceinput/choiceprovider/ScriptingChoices.java#L39-L70","documentation":"ScriptingChoices.getChoices executes a script to produce choice options and expects the script result to be a Map (value->label) or a List of strings. Any other result type throws an ExplicitException. This enforces a strict return contract for dynamic choice providers.","triggerScenarios":"Running a scripting choice provider whose script returns a value that is neither Map nor List (e.g. a single String, Integer, or null produced by the script) and where the call is not allPossible (allPossible==false path throws; the allPossible path logs and returns an empty map).","commonSituations":"Groovy/JavaScript choice script written to print or return a plain string instead of a list/map; script refactored to return a custom object; forgetting to wrap results in a map when labels are needed.","solutions":["Change the script to return a Map of value->label pairs, e.g. return [dev: \"Development\", prod: \"Production\"].","Or return a List of strings when labels are unnecessary: return [\"dev\", \"prod\"].","Ensure the script's last expression is the return value (Groovy implicit return) and no early return returns null.","Wrap the result if the underlying API returns a single object: put it in a list or map before returning."],"exampleFix":"// before (Groovy choice script)\ndef env = systemProps['env'];\nreturn env;\n// after\nreturn [env: env];\n// or\nreturn [env];","handlingStrategy":"type-guard","validationCode":"// inside the choice script (Groovy)\ndef result = computeChoices()\nif (!(result instanceof Map) && !(result instanceof List))\n    throw new IllegalStateException(\"Choice script must return Map or List, got \" + result?.getClass())","typeGuard":"boolean isValidScriptResult(Object result) {\n    return result instanceof Map || result instanceof List;\n}","tryCatchPattern":"try {\n    Map<String, String> choices = scriptingChoices.getChoices(false);\n} catch (ExplicitException e) {\n    logger.error(\"Choice script returned wrong type\", e);\n    choices = Collections.emptyMap();\n}","preventionTips":["Return a Map<String,String> when custom labels are needed, otherwise a List<String>.","In Groovy, ensure the last expression is the intended return value (implicit returns).","Test the script in the script console before wiring it into the build spec.","Never return null or a single scalar from a choice script."],"tags":["scripting","choice-provider","return-type"],"backgroundTag":"unexpected-api-response-shape","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"}