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
- Log the full error message — the status and body text identify the exact cause (401 = auth, 429 = rate limit, 400 = bad payload).
- Verify process.env.MORPH_API_KEY is set and valid in the environment running the code.
- Check the payload: confirm the model name and edit format match the current Morph API docs.
- Retry with exponential backoff for 429/5xx; treat 4xx as non-retryable.
- 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
- Validate MORPH_API_KEY is present and valid at startup, not on first request.
- Retry only 429/5xx with exponential backoff; never blind-retry 4xx.
- Keep the model name in configuration and validate it against current Morph docs.
- Log the response body (already embedded in the message) for every failure to speed diagnosis.
- Implement a non-Morph fallback path for edits when the service is degraded.
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
- Failed to install packages: ${response.statusText}
- Firecrawl API error: ${error}
- Failed to scrape content
- Failed to scrape website
- Unknown error
AI-assisted analysis of firecrawl/open-lovable@69bd93bae7 (2026-08-28).
Data as JSON: /api/errors/17cb0f68f5553bde.
Report an issue: GitHub.