firecrawl/open-lovable · error · Error

Morph API error ${res.status}: ${text}

Error message

Morph API error ${res.status}: ${text}

What it means

morphChatCompletionsCreate calls the Morph apply-model HTTP API and throws this error whenever the HTTP response is not ok (res.ok is false). It embeds the HTTP status code and the raw response body text so the caller can see exactly why Morph rejected the request. It is an intentional wrapper around any 4xx/5xx from the Morph endpoint, not a bug in this library.

Source

Thrown at lib/morph-fast-apply.ts:54

  }

  const fullPath = `/home/user/app/${normalizedPath}`;
  return { normalizedPath, fullPath };
}

async function morphChatCompletionsCreate(payload: any) {
  if (!process.env.MORPH_API_KEY) throw new Error('MORPH_API_KEY is not set');
  const res = await fetch('https://api.morphllm.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.MORPH_API_KEY}`
    },
    body: JSON.stringify(payload)
  });
  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Morph API error ${res.status}: ${text}`);
  }
  return res.json();
}

// Parse <edit> blocks from LLM output
export function parseMorphEdits(text: string): MorphEditBlock[] {
  const edits: MorphEditBlock[] = [];
  const editRegex = /<edit\s+target_file="([^"]+)">([\s\S]*?)<\/edit>/g;
  let match: RegExpExecArray | null;
  while ((match = editRegex.exec(text)) !== null) {
    const targetFile = match[1].trim();
    const inner = match[2];
    const instrMatch = inner.match(/<instructions>([\s\S]*?)<\/instructions>/);
    const updateMatch = inner.match(/<update>([\s\S]*?)<\/update>/);
    const instructions = instrMatch ? instrMatch[1].trim() : '';
    const update = updateMatch ? updateMatch[1].trim() : '';
    if (targetFile && update) {
      edits.push({ targetFile, instructions, update });

View on GitHub (pinned to 69bd93bae7)

Solutions

  1. Log the full error message — the status and body text identify the exact cause (401 = auth, 429 = rate limit, 400 = bad payload).
  2. Verify process.env.MORPH_API_KEY is set and valid in the environment running the code.
  3. Check the payload: confirm the model name and edit format match the current Morph API docs.
  4. Retry with exponential backoff for 429/5xx; treat 4xx as non-retryable.
  5. Wrap the call in try/catch and fall back to direct file writes if Morph is unavailable.

Example fix

// before
const res = await fetch(...);
if (!res.ok) {
  const text = await res.text();
  throw new Error(`Morph API error ${res.status}: ${text}`);
}
// after
const res = await fetch(...);
if (!res.ok) {
  const text = await res.text();
  if (res.status === 429 || res.status >= 500) {
    // retry with backoff before surfacing
    return retryWithBackoff(() => morphChatCompletionsCreate(payload), 3);
  }
  throw new Error(`Morph API error ${res.status}: ${text}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!process.env.MORPH_API_KEY) {
  throw new Error('MORPH_API_KEY is not set; Morph API calls will fail with 401');
}
const res = await fetch(url, { method: 'HEAD' }); // optional preflight availability probe

Try / catch

try {
  const data = await morphChatCompletionsCreate(payload);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Morph API error')) {
    const status = err.message.match(/Morph API error (\d+)/)?.[1];
    if (status === '429' || Number(status) >= 500) {
      return retryWithBackoff(() => morphChatCompletionsCreate(payload));
    }
    console.error(`Morph request failed (${status}): ${err.message}`);
    return fallbackToLocalApply(payload);
  }
  throw err;
}

Prevention

When it happens

Trigger: MORPH_API_KEY is missing/invalid (401/403), the payload is malformed (400), model name is wrong (404), rate limits are hit (429), or the Morph service returns 5xx; any non-ok fetch response from the Morph completions endpoint triggers it, with the body text included in the message.

Common situations: Deployments where MORPH_API_KEY env var is unset or stale; exceeding Morph rate limits during batch file edits; typos in the model field of the payload; Morph API downtime or gateway errors (502/503) during incident windows.

Related errors


AI-assisted analysis of firecrawl/open-lovable@69bd93bae7 (2026-08-28). Data as JSON: /api/errors/17cb0f68f5553bde. Report an issue: GitHub.