different-ai/openwork · error · ApiError

invalid_payload

invalid_payload

Error message

providers must be an array of non-empty strings

What it means

This 400 invalid_payload error is thrown by parseDisabledProvidersPayload when the request body is not a JSON array. The disabled-providers update endpoint expects an array of provider name strings, so any other top-level shape (object, string, null, missing body) is rejected before any validation of elements occurs.

Source

Thrown at apps/server/src/server.ts:286

}

function runtimeConfigKeys(config: RuntimeOpencodeConfig): string[] {
  const keys: string[] = [];
  if (config.default_agent) keys.push("default_agent");
  if (Array.isArray(config.plugin) && config.plugin.length) keys.push("plugin");
  if (Array.isArray(config.disabled_providers) && config.disabled_providers.length) keys.push("disabled_providers");
  if (isRecord(config.mcp) && Object.keys(config.mcp).length) keys.push("mcp");
  const permission = isRecord(config.permission) ? config.permission : null;
  if (permission && isRecord(permission.external_directory) && Object.keys(permission.external_directory).length) {
    keys.push("permission");
  }
  if (isRecord(config.provider) && Object.keys(config.provider).length) keys.push("provider");
  return keys;
}

function parseDisabledProvidersPayload(value: unknown): string[] {
  if (!Array.isArray(value)) {
    throw new ApiError(400, "invalid_payload", "providers must be an array of non-empty strings");
  }
  const providers: string[] = [];
  for (const entry of value) {
    if (typeof entry !== "string" || !entry.trim()) {
      throw new ApiError(400, "invalid_payload", "providers must be an array of non-empty strings");
    }
    const provider = entry.trim();
    if (!providers.includes(provider)) providers.push(provider);
  }
  return providers;
}

function parseRuntimeProviderPatchPayload(body: Record<string, unknown>): Record<string, unknown> {
  const provider = body.provider;
  if (!isRecord(provider)) {
    throw new ApiError(400, "invalid_payload", "provider must be an object");
  }
  for (const [providerId, value] of Object.entries(provider)) {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Send a raw JSON array as the body: ["anthropic", "openai"]
  2. Ensure Content-Type: application/json and JSON.stringify the array
  3. Check API docs for the exact endpoint payload shape

Example fix

// before
await fetch(url, { body: JSON.stringify({ providers: ["anthropic"] }) });
// after
await fetch(url, { body: JSON.stringify(["anthropic"]), headers: { "Content-Type": "application/json" } });
Defensive patterns

Strategy: validation

Validate before calling

if (!Array.isArray(providers)) throw new Error("providers must be a JSON array");

Type guard

const isStringArray = (v: unknown): v is string[] => Array.isArray(v) && v.every(x => typeof x === "string");

Try / catch

try {
  await setDisabledProviders(body);
} catch (e) {
  if (e.code === "invalid_payload") console.error("send a raw array: [\"anthropic\"]", e.message);
  else throw e;
}

Prevention

When it happens

Trigger: Calling the disabled-providers PATCH/PUT endpoint with body that is not an array, e.g. {"providers": ["anthropic"]} instead of ["anthropic"], or an empty/absent body.

Common situations: Wrapping the list in an object by mistake; sending form-encoded or stringified JSON with wrong Content-Type; older clients using a different payload shape.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/9fc82b567b69d18a. Report an issue: GitHub.