ComposioHQ/composio · error · Error

proxy fetch only supports GET, POST, PUT, DELETE, PATCH.

Error message

proxy fetch only supports GET, POST, PUT, DELETE, PATCH.

What it means

The proxy fetch layer only forwards GET, POST, PUT, DELETE, and PATCH (after uppercasing init.method; default GET). normalizeFetchInput rejects any other HTTP verb such as HEAD, OPTIONS, or custom methods.

Source

Thrown at ts/packages/cli/src/services/run-helpers-runtime.ts:433

  if (ArrayBuffer.isView(body)) {
    return encodeBase64(new Uint8Array(body.buffer, body.byteOffset, body.byteLength));
  }
  return body;
};

const normalizeFetchInput = async (input: unknown, init: RequestInit = {}) => {
  if (typeof Request !== 'undefined' && input instanceof Request) {
    throw new Error(
      'proxy() does not support passing a Request instance yet. Pass a URL string and init instead.'
    );
  }
  const endpoint = input instanceof URL ? input.toString() : input;
  if (typeof endpoint !== 'string' || endpoint.trim().length === 0) {
    throw new Error('proxy fetch requires a non-empty URL string or URL object.');
  }
  const method = typeof init.method === 'string' ? init.method.toUpperCase() : 'GET';
  if (!['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(method)) {
    throw new Error('proxy fetch only supports GET, POST, PUT, DELETE, PATCH.');
  }
  return {
    endpoint: endpoint.trim(),
    method,
    parameters: normalizeFetchHeaders(init.headers),
    body: await normalizeFetchBody(init.body),
  };
};

const toProxyResponse = async (result: ProxyExecuteResponse) => {
  const headers = new Headers(result?.headers || {});
  if (result?.binary_data?.url) {
    const binaryResponse = await fetch(result.binary_data.url);
    binaryResponse.headers.forEach((value, key) => {
      if (!headers.has(key)) headers.set(key, value);
    });
    return new Response(binaryResponse.body, {
      status: result.status ?? binaryResponse.status,

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Use one of GET/POST/PUT/DELETE/PATCH (case-insensitive)
  2. For HEAD, issue a GET and ignore the body, or use a direct fetch outside the proxy
  3. Validate the method against the allowlist before calling proxy.fetch when it comes from user input

Example fix

// before
await proxy.fetch(url, { method: 'HEAD' });
// after
await proxy.fetch(url, { method: 'GET' }); // ignore response body if unneeded
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = ['GET','POST','PUT','DELETE','PATCH'];
if (!ALLOWED.includes((method ?? 'GET').toUpperCase())) throw new RangeError(`method must be one of ${ALLOWED.join(',')}`);

Type guard

const isAllowedMethod = (m: string): m is 'GET'|'POST'|'PUT'|'DELETE'|'PATCH' => ['GET','POST','PUT','DELETE','PATCH'].includes(m.toUpperCase());

Prevention

When it happens

Trigger: Calling proxy.fetch(url, { method: 'HEAD' }), { method: 'OPTIONS' }, or a lowercased verb that is still unsupported ('trace', 'connect').

Common situations: Health checks using HEAD, CORS preflight emulation with OPTIONS, or generic fetch wrappers that pass through arbitrary caller-supplied methods.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/d47a728475058c5e. Report an issue: GitHub.