can1357/oh-my-pi · error · Error
Capability "${def.id}" is already defined
Error message
Capability "${def.id}" is already defined What it means
defineCapability() stores capability definitions in a module-level registry keyed by id. Calling it twice with the same id would silently overwrite the first definition, so the registry throws. This protects the unique-id invariant that registerProvider and loadCapability rely on.
Source
Thrown at packages/coding-agent/src/capability/index.ts:54
/** Provider display metadata (shared across capabilities) */
const providerMeta = new Map<string, { displayName: string; description: string }>();
/** Disabled providers (by ID) */
const disabledProviders = new Set<string>();
/** Settings manager for persistence (if set) */
let settings: Settings | null = null;
// =============================================================================
// Registration API
// =============================================================================
/**
* Define a new capability.
*/
export function defineCapability<T>(def: Omit<Capability<T>, "providers">): Capability<T> {
if (capabilities.has(def.id)) {
throw new Error(`Capability "${def.id}" is already defined`);
}
const capability: Capability<T> = { ...def, providers: [] };
capabilities.set(def.id, capability as Capability<unknown>);
return capability;
}
/**
* Register a provider for a capability.
*/
export function registerProvider<T>(capabilityId: string, provider: Provider<T>): void {
const capability = capabilities.get(capabilityId);
if (!capability) {
throw new Error(`Unknown capability: "${capabilityId}". Define it first with defineCapability().`);
}
// Store provider metadata (for cross-capability display)
if (!providerMeta.has(provider.id)) {
providerMeta.set(provider.id, {View on GitHub (pinned to 9690622007)
Solutions
- Rename your capability id to a unique namespaced value (e.g. 'myplugin.myCapability')
- Check whether the module containing the defineCapability call is loaded twice (duplicate bundling, double registration) and fix the loading path
- If re-defining is intentional, wrap the call in try/catch or look up the existing capability first
Example fix
// before
defineCapability({ id: "hooks", ... });
// after
defineCapability({ id: "myplugin.hooks", ... }); Defensive patterns
Strategy: try-catch
Validate before calling
// idempotent define
const CAPABILITY_ID = "myplugin.hooks";
if (!isCapabilityDefined(CAPABILITY_ID)) {
defineCapability({ id: CAPABILITY_ID, displayName: "My Hooks" /* ... */ });
} Try / catch
let capability: Capability<Hooks>;
try {
capability = defineCapability({ id: "myplugin.hooks", displayName: "My Hooks" });
} catch (err) {
if (err instanceof Error && err.message.includes('already defined')) {
capability = getCapability<Hooks>("myplugin.hooks");
} else throw err;
} Prevention
- Namespace capability ids with your plugin/package name
- Define capabilities once in a dedicated module imported for side effects only once
- Watch for duplicate module evaluation in bundling
- Never copy another definition's id without renaming
When it happens
Trigger: Calling defineCapability({ id: "existing-id", ... }) when a capability with that id was already defined — typically module-level definitions evaluated twice, two modules defining the same id, or module re-import/duplicate-bundle evaluation.
Common situations: A bundler duplicates a module so its top-level defineCapability call runs twice; a plugin copies another capability's definition without changing the id; an extension is loaded twice under different names.
Related errors
- Unknown capability: "${capabilityId}". Define it first with
- Destination option ${key} must be a string
- Unknown capability: "${capabilityId}"
- Unknown auth-broker action: ${String(_exhaustive)}
- Unknown auth-gateway action: ${String(_exhaustive)}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a7edf2394b5f8a73.
Report an issue: GitHub.