theonedev/onedev · error · ExplicitException
Script should return either a Map or a List
Error message
Script should return either a Map or a List
What it means
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.
Source
Thrown at server-core/src/main/java/io/onedev/server/buildspecmodel/inputspec/choiceinput/choiceprovider/ScriptingChoices.java:57
}
@SuppressWarnings("unchecked")
@Override
public Map<String, String> getChoices(boolean allPossible) {
Map<String, Object> variables = new HashMap<>();
variables.put("allPossible", allPossible);
try {
Object result = GroovyUtils.evalScriptByName(scriptName, variables);
if (result instanceof Map) {
return (Map<String, String>) result;
} else if (result instanceof List) {
Map<String, String> choices = new HashMap<>();
for (String item: (List<String>)result)
choices.put(item, null);
return choices;
} else {
throw new ExplicitException("Script should return either a Map or a List");
}
} catch (RuntimeException e) {
if (allPossible) {
logger.error("Error getting all possible choices", e);
return new HashMap<>();
} else {
throw e;
}
}
}
}
View on GitHub (pinned to d44925c47c)
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.
Example fix
// before (Groovy choice script) def env = systemProps['env']; return env; // after return [env: env]; // or return [env];
Defensive patterns
Strategy: type-guard
Validate before calling
// inside the choice script (Groovy)
def result = computeChoices()
if (!(result instanceof Map) && !(result instanceof List))
throw new IllegalStateException("Choice script must return Map or List, got " + result?.getClass()) Type guard
boolean isValidScriptResult(Object result) {
return result instanceof Map || result instanceof List;
} Try / catch
try {
Map<String, String> choices = scriptingChoices.getChoices(false);
} catch (ExplicitException e) {
logger.error("Choice script returned wrong type", e);
choices = Collections.emptyMap();
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Error evaluating groovy script:
- Unauthorized groovy script:
- Groovy script not found:
- Error evaluating groovy script:
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/861546366d635eb9.
Report an issue: GitHub.