apple/pkl · error
commandSubcommandConflict
commandSubcommandConflict
Error message
commandSubcommandConflict
What it means
CommandSpecParser.collectSubcommands throws commandSubcommandConflict when two subcommands declared in a Pkl CLI command's `subcommands` mapping have the same name. parse() builds the parent spec, and the duplicate member's source section is used for the error. The CLI cannot dispatch ambiguously, so the spec is rejected at build time.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java:141
// NB: these next two lines are the only place where we lose type safety.
// SubcommandState is type-erased to Object in the public API to hide internals.
// Consumers of this API must ensure CommandSpec.apply is only ever passed an
// instance of CommandSpec.State previously returned from a prior invocation
// of CommandSpec.apply.
parent == null ? null : (SubcommandState) parent.contents()),
(it) -> handleErrors(() -> evaluateResult(command, (SubcommandState) it))));
}
private List<CommandSpec> collectSubcommands(VmTyped commandInfo) {
var subcommands = new ArrayList<CommandSpec>();
var subcommandNames = new HashSet<String>();
var subcommandsProperty = (VmObject) VmUtils.readMember(commandInfo, Identifier.SUBCOMMANDS);
subcommandsProperty.force(false, false);
subcommandsProperty.iterateAlreadyForcedMemberValues(
(key, member, value) -> {
var spec = parse((VmTyped) value);
if (subcommandNames.contains(spec.name())) {
throw exceptionBuilder()
.withSourceSection(member.getSourceSection())
.evalError(
"commandSubcommandConflict",
VmUtils.readMember(commandInfo, Identifier.NAME),
spec.name())
.build();
}
subcommandNames.add(spec.name());
subcommands.add(spec);
return true;
});
return subcommands;
}
// region options handling
private VmClass getOptionsClass(VmTyped command) {View on GitHub (pinned to f3efcbfc9b)
Solutions
- Rename one of the conflicting subcommands so names are unique within the command
- Remove the duplicated subcommand block
- If generating subcommands from data, deduplicate names before emitting the Pkl source
Example fix
// before
subcommands {
["build"] { ... }
["build"] { ... }
}
// after
subcommands {
["build"] { ... }
["test"] { ... }
} Defensive patterns
Strategy: validation
Validate before calling
const names = Object.keys(subcommandsSpec);
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
if (dupes.length) throw new Error(`duplicate subcommand names: ${dupes}`); Try / catch
try { parseCommandSpec(pklSource) } catch (e) {
if (String(e).includes("commandSubcommandConflict")) { /* fix duplicate name in pkl */ }
} Prevention
- Deduplicate generated subcommand names before emitting Pkl
- Review pasted subcommand blocks for leftover duplicates
- Give each subcommand a unique, action-specific name
When it happens
Trigger: Declaring `subcommands { ["deploy"] { ... }; ["deploy"] { ... } }` — two members whose parsed CommandSpec.name() collides within one command's subcommands object.
Common situations: Copy-pasting a subcommand block and forgetting to rename it; generating subcommands programmatically from a list with duplicate keys/names.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- commandFlagNameCollision
- +e.getMessage()
- commandOptionsTypeNotClass
- commandOptionsTypeAbstractClass
- commandOptionBothFlagAndArgument
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/0a10c3b1fcb07887.
Report an issue: GitHub.