paperclipai/paperclip · error · Error
Invalid category '${options.category}'. Expected one of: ${[
Error message
Invalid category '${options.category}'. Expected one of: ${[...VALID_CATEGORIES].join(", ")} What it means
Thrown by scaffoldPluginProject() when options.category is supplied but is not one of the allowed plugin categories. VALID_CATEGORIES is the set connector, workspace, automation, ui, environment (defined at packages/plugins/create-paperclip-plugin/src/index.ts:8). The check is skipped when category is omitted, in which case a default is derived from the chosen template.
Source
Thrown at packages/plugins/create-paperclip-plugin/src/index.ts:134
/**
* Generate a complete Paperclip plugin starter project.
*
* Output includes manifest/worker/UI entries, SDK harness tests, bundler presets,
* and a local dev server script for hot-reload workflow.
*/
export function scaffoldPluginProject(options: ScaffoldPluginOptions): string {
const template = options.template ?? "default";
if (!VALID_TEMPLATES.includes(template)) {
throw new Error(`Invalid template '${template}'. Expected one of: ${VALID_TEMPLATES.join(", ")}`);
}
if (!isValidPluginName(options.pluginName)) {
throw new Error("Invalid plugin name. Must be lowercase and may include scope, dots, underscores, or hyphens.");
}
if (options.category && !VALID_CATEGORIES.has(options.category)) {
throw new Error(`Invalid category '${options.category}'. Expected one of: ${[...VALID_CATEGORIES].join(", ")}`);
}
const outputDir = path.resolve(options.outputDir);
if (fs.existsSync(outputDir)) {
throw new Error(`Directory already exists: ${outputDir}`);
}
const displayName = options.displayName ?? makeDisplayName(options.pluginName);
const description = options.description ?? "A Paperclip plugin";
const author = options.author ?? "Plugin Author";
const category = options.category ?? (template === "workspace" ? "workspace" : template === "environment" ? "environment" : "connector");
const manifestId = packageToManifestId(options.pluginName);
const localSdkPath = path.resolve(options.sdkPath ?? getLocalSdkPackagePath());
const localSharedPath = getLocalSharedPackagePath(localSdkPath);
const repoRoot = getRepoRootFromSdkPath(localSdkPath);
const useWorkspaceSdk = isInsideDir(outputDir, repoRoot);
fs.mkdirSync(outputDir, { recursive: true });View on GitHub (pinned to 67001ec6eb)
Solutions
- Pass one of: connector, workspace, automation, ui, environment (exact lowercase).
- Omit category entirely and let it default from the template (workspace -> workspace, environment -> environment, otherwise connector).
- If you need a new category, add it to VALID_CATEGORIES in packages/plugins/create-paperclip-plugin/src/index.ts:8 and update the ScaffoldPluginOptions type at line 17 before scaffolding.
Example fix
// before
scaffoldPluginProject({ pluginName: "@acme/foo", outputDir: "./foo", category: "Connectors" });
// after
scaffoldPluginProject({ pluginName: "@acme/foo", outputDir: "./foo", category: "connector" }); Defensive patterns
Strategy: validation
Validate before calling
import { scaffoldPluginProject } from "@paperclipai/create-paperclip-plugin";
const VALID_CATEGORIES = new Set(["connector", "workspace", "automation", "ui", "environment"]]);
function assertCategory(cat: string | undefined): cat is "connector" | "workspace" | "automation" | "ui" | "environment" {
return cat == null || VALID_CATEGORIES.has(cat);
}
if (!assertCategory(opts.category)) {
throw new Error(`Unsupported category; pick one of: ${[...VALID_CATEGORIES].join(", ")}`);
} Type guard
function isPluginCategory(v: unknown): v is "connector" | "workspace" | "automation" | "ui" | "environment" {
return typeof v === "string" && ["connector","workspace","automation","ui","environment"].includes(v);
} Prevention
- Validate category against the documented set before calling scaffoldPluginProject().
- Omit category to use the template-derived default when unsure.
- Keep VALID_CATEGORIES in sync with the ScaffoldPluginOptions type.
When it happens
Trigger: Calling scaffoldPluginProject({ pluginName, outputDir, category: "data" }) or any category string outside the allowed set. Also triggered by passing a pluralized or misspelled value like "connectors", "UI", or "Automation" (case-sensitive — only lowercase values match).
Common situations: Typos and casing mistakes (e.g. "Connectors", "UI"). Assuming a category exists because it is a valid template name (template "default" has no matching category). Copy-pasting a category from an older plugin version after the catalog changed.
Related errors
- Company ID is required. Pass --company-id, set PAPERCLIP_COM
- Source and target Paperclip configs are the same. Pass --fro
- Resolved worktree path ${directPath} does not contain .paper
- Resolved worktree "${selector}" does not look like a Papercl
- Hostname is required
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/33603ee94567252e.
Report an issue: GitHub.