can1357/oh-my-pi · error · Error

Unknown capability: "${capabilityId}". Define it first with

Error message

Unknown capability: "${capabilityId}". Define it first with defineCapability().

What it means

registerProvider() looks up the capability by id in the registry and throws if no capability was previously created via defineCapability(). Providers can only attach to capabilities that exist; this ordering is enforced at registration time.

Source

Thrown at packages/coding-agent/src/capability/index.ts:67

/**
 * 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, {
			displayName: provider.displayName,
			description: provider.description,
		});
	}

	// Track which capabilities this provider is registered for
	if (!providerCapabilities.has(provider.id)) {
		providerCapabilities.set(provider.id, new Set());
	}
	providerCapabilities.get(provider.id)!.add(capabilityId);

	// Insert in priority order (highest first)
	const providers = capability.providers as Provider<T>[];

View on GitHub (pinned to 9690622007)

Solutions

  1. Import and call defineCapability for the id before registerProvider — usually by importing the capability object and passing its .id
  2. Verify the id string matches the defined capability exactly (typos, renames)
  3. Fix import order so the capability module's side effects run first
  4. Better: pass the typed capability object instead of a raw string so the id comes from the definition

Example fix

// before
registerProvider("hook", myProvider);
// after
import { hookCapability } from "./capabilities";
registerProvider(hookCapability.id, myProvider);
Defensive patterns

Strategy: validation

Validate before calling

import { capabilities } from "@oh-my-pi/pi-coding-agent/capability";
if (!capabilities.has(hookCapability.id)) {
  throw new Error("capability module not loaded; import it before registering providers");
}
registerProvider(hookCapability.id, provider);

Try / catch

try {
  registerProvider(capabilityId, provider);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Unknown capability')) {
    console.error(`capability "${capabilityId}" undefined — check defineCapability order/ids`);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling registerProvider("someId", provider) before (or without ever) calling defineCapability({ id: "someId" }) — e.g. wrong capability id string, typo, or module load order where the provider module is imported before the capability module.

Common situations: A typo in the capability id string; import hoisting/order issues where provider registration runs before the capability definition module; renaming a capability but not updating provider registration call sites.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c7469ab26b520f40. Report an issue: GitHub.