musistudio/claude-code-router · warning

[plugin:${transform.pluginId}] Request transform ${transform

Error message

[plugin:${transform.pluginId}] Request transform ${transform.id} failed: ${formatError(error)}

What it means

Logged by applyGatewayRequestTransforms when a single registered request transform (plugin transform) throws while processing a request. The transform's result is discarded (continue) and the remaining transforms still run; the request proceeds without that transform's modifications (e.g. no rerouting/rewriting).

Source

Thrown at packages/core/src/plugins/service.ts:348

    for (const transform of this.gatewayRequestTransforms) {
      const beforeBody = body;
      const beforeHeaders = headers;
      const beforeRoutedModel = routedModel;
      let result: GatewayPluginRequestTransformResult | null | undefined | false;
      try {
        result = await transform.transform({
          body: cloneJsonObject(body),
          headers: { ...headers },
          method: input.method,
          path: input.path,
          requestId: input.requestId,
          ...(routedModel ? { routedModel } : {}),
          ...(input.sessionId ? { sessionId: input.sessionId } : {}),
          ...(input.tokenCount !== undefined ? { tokenCount: input.tokenCount } : {}),
          url: input.url
        }, this.createRequestTransformContext(transform.pluginId));
      } catch (error) {
        console.warn(`[plugin:${transform.pluginId}] Request transform ${transform.id} failed: ${formatError(error)}`);
        continue;
      }
      if (!result || !isRecord(result)) {
        continue;
      }

      const changes: RequestRouteTraceChange[] = [];
      const nextBody = isRecord(result.body) ? cloneJsonObject(result.body) : body;
      if (nextBody && nextBody !== beforeBody && JSON.stringify(nextBody) !== JSON.stringify(beforeBody)) {
        body = nextBody;
        changes.push({ operation: beforeBody ? "replace" : "add", path: "/body", scope: "body" });
      }

      const nextHeaders = applyHeaderPatch(headers, result.headers);
      headers = nextHeaders.headers;
      changes.push(...nextHeaders.changes(beforeHeaders));

      if (typeof result.routedModel === "string" && result.routedModel.trim() && result.routedModel !== beforeRoutedModel) {

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Pin down which transform from the log (pluginId + transform id) and reproduce with the exact request shape.
  2. Make the transform defensive: check input.body/model before acting and never throw.
  3. Update the plugin to the version matching your core's transform input contract.
  4. If the transform is optional, disable it until fixed — requests pass through unaffected.

Example fix

// before
const model = input.body.model as string; // throws if absent
// after
const model = (input.body as Record<string, unknown>)?.model;
if (typeof model !== 'string') return undefined;
Defensive patterns

Strategy: try-catch

Validate before calling

const body = input.body as Record<string, unknown> | undefined;
if (!body || typeof body.model !== 'string') {
  // skip relying on this transform
}

Type guard

const isTransformSafeInput = (i: unknown): i is { body: Record<string, unknown>; url: string } =>
  typeof i === 'object' && i !== null && typeof (i as any).url === 'string' &&
  (i as any).body !== undefined;

Try / catch

null

Prevention

When it happens

Trigger: A gateway request passes through a plugin transform whose callback throws — bad input shape assumption (missing body/model), a bug in URL manipulation, or context method misuse. Called via pluginTransform during request handling.

Common situations: Transform assumes JSON body but receives streaming/empty body; transform written against an older input contract after core upgrade; null sessionId/tokenCount edge cases (the code spreads those conditionally).

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/23eaa8e0adb25863. Report an issue: GitHub.