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

  1. Rename one of the conflicting subcommands so names are unique within the command
  2. Remove the duplicated subcommand block
  3. 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

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


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/0a10c3b1fcb07887. Report an issue: GitHub.