github/copilot-sdk · error · IllegalArgumentException

BuiltinPluginDirectories must contain only absolute paths…

Error message

BuiltinPluginDirectories must contain only absolute paths: + path

What it means

CopilotClientOptions.setBuiltinPluginDirectories validates that every supplied Path is absolute before storing it. A relative path would resolve against an unpredictable working directory at client start, so the builder rejects the whole list with IllegalArgumentException naming the offending path.

Solutions

  1. Resolve relative paths against a known base before passing them: path.toAbsolutePath() (or base.resolve(path)).
  2. Check each path with Path.isAbsolute() in your config loader and fail/normalize early.
  3. Anchor config values to a documented base directory (e.g. install dir or $HOME) and document it.
  4. If you intend an empty set, pass an empty collection or null — the builder supports clearing the list.

Example fix

// before
builder.setBuiltinPluginDirectories(List.of(Path.of("plugins/builtin")));

// after
Path dir = Path.of("plugins/builtin").toAbsolutePath();
builder.setBuiltinPluginDirectories(List.of(dir));
Defensive patterns

Strategy: validation

Validate before calling

List<Path> safe = paths.stream()
    .peek(p -> Objects.requireNonNull(p, "plugin dir must not be null"))
    .map(Path::toAbsolutePath)
    .collect(Collectors.toList());
options.setBuiltinPluginDirectories(safe);

Type guard

static boolean allAbsolute(List<Path> paths) {
    return paths != null && paths.stream().allMatch(Objects::nonNull) &&
           paths.stream().allMatch(Path::isAbsolute);
}

Try / catch

try {
    builder.setBuiltinPluginDirectories(paths);
} catch (IllegalArgumentException e) {
    Path bad = Path.of(e.getMessage().substring(e.getMessage().lastIndexOf(': ') + 2));
    builder.setBuiltinPluginDirectories(List.of(bad.toAbsolutePath()));
}

Prevention

When it happens

Trigger: Calling setBuiltinPluginDirectories with a list containing at least one relative path, e.g. Path.of("plugins/builtin") or Paths.get("./ext"), or paths built from user-relative config strings.

Common situations: Config files storing plugin directories relative to a project root; building Paths from CLI args without resolution; environment-dependent working directories making relative paths behave differently across machines.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/8b964561076f0473. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java:153

    /**
     * Sets trusted plugin directories bundled by the host. Every path must be
     * absolute. When non-empty, the complete set is registered during startup
     * before sessions can be created.
     *
     * @param paths
     *            absolute plugin directory paths, or {@code null}/empty to disable
     * @return this options instance for method chaining
     */
    public CopilotClientOptions setBuiltinPluginDirectories(List<Path> paths) {
        if (paths == null || paths.isEmpty()) {
            this.builtinPluginDirectories = null;
            return this;
        }
        for (Path path : paths) {
            Objects.requireNonNull(path, "builtin plugin directory path must not be null");
            if (!path.isAbsolute()) {
                throw new IllegalArgumentException(
                        "BuiltinPluginDirectories must contain only absolute paths: " + path);
            }
        }
        this.builtinPluginDirectories = new ArrayList<>(paths);
        return this;
    }

    /**
     * Gets the extra CLI arguments.
     * <p>
     * Returns a shallow copy of the internal array, or {@code null} if no arguments
     * have been set.
     *
     * @return a copy of the extra arguments, or {@code null}
     */
    public String[] getCliArgs() {
        return cliArgs != null ? Arrays.copyOf(cliArgs, cliArgs.length) : null;
    }

View on GitHub (pinned to cd8cf15dc3)