apple/pkl · error · BadValue
+e.getMessage()
Error message
+e.getMessage()
What it means
CommandSpecParser.handleBadValue wraps any Throwable from value conversion/transform functions into a BadValue whose message is a newline plus e.getMessage(). The reported message is therefore just the underlying exception's message, reformatted so it prints neatly under a CLI 'invalid value for <name>:' line. The real cause is whatever the user-provided convert/transform function threw.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java:1081
// endregion
// region dynamic import handling
private static boolean isImport(VmTyped value) {
return value.getVmClass() == CommandModule.getImportClass();
}
private static boolean isImport(Object value) {
return value instanceof VmTyped vmTyped
&& vmTyped.getVmClass() == CommandModule.getImportClass();
}
// handle errors from convert/transformAll and correctly format them for the CLI
private Object handleBadValue(Supplier<Object> f) {
try {
return handleErrors(f);
} catch (Throwable e) {
// add a newline so this prints nicely under "Error: invalid value for <name>:"
throw new BadValue("\n" + e.getMessage());
}
}
private <T> T handleErrors(Supplier<T> f) {
try {
return f.get();
} catch (VmStackOverflowException e) {
if (VmUtils.isPklBug(e)) {
throw new VmExceptionBuilder()
.bug("Stack overflow")
.withCause(e.getCause())
.build()
.toPklException(frameTransformer, color);
}
throw e.toPklException(frameTransformer, color);
} catch (VmException e) {
throw e.toPklException(frameTransformer, color);
} catch (Exception e) {View on GitHub (pinned to f3efcbfc9b)
Solutions
- Read the wrapped message under 'invalid value for <name>:' to identify the failing conversion.
- Fix or guard the convert/transformAll function in the command spec so invalid input yields a clear message.
- Add explicit input validation in the converter that throws a descriptive message instead of an incidental exception.
Example fix
// before
convert { it.toInt() } // NumberFormatException surfaces raw
// after
convert { it.toIntOrNull() ?: throw IllegalArgumentException("expected an integer, got: $it") } Defensive patterns
Strategy: try-catch
Validate before calling
// inside a command spec converter
convert { v -> require(v.isNotBlank()) { "value must not be blank" }; v } Try / catch
try {
runConversion(value)
} catch (e: BadValue) {
logger.error("invalid value for ${flagName}: ${e.message}")
} Prevention
- Validate flag values inside convert blocks with descriptive messages
- Test converters with malformed inputs
- Avoid throwing raw runtime exceptions from converters
When it happens
Trigger: A CLI flag/option value fails conversion: the spec's convert or transformAll function throws (custom converters, parse failures) while parsing command-spec-based CLI arguments.
Common situations: Custom `convert { }` blocks in command specs throwing on bad user input; split() or toInt() style failures in transform functions; exceptions thrown by user-provided validation code inside a spec.
Understand the failure class
Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.
Related errors
- commandSubcommandConflict
- commandOptionsTypeNotClass
- commandOptionsTypeAbstractClass
- commandOptionBothFlagAndArgument
- commandArgumentsMultipleRepeated
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/22a58b9122d22de8.
Report an issue: GitHub.