How to Fix 'JSON parsing error' in CrewAI (TypeScript)

By Cyprian AaronsUpdated 2026-04-21
json-parsing-errorcrewaitypescript

What the error means

JSON parsing error in CrewAI usually means the agent returned text that the framework tried to parse as JSON, but the output was not valid JSON. In TypeScript projects, this shows up most often when you expect structured output from a task or tool, but the model responds with markdown, extra commentary, or malformed JSON.

You’ll typically hit this when using output_json, a structured TaskOutput, or a tool that expects strict JSON and gets back something like:

CrewAIError: JSON parsing error: Unexpected token '`', "```json ..." is not valid JSON

The Most Common Cause

The #1 cause is asking the LLM for JSON but not enforcing a strict schema, then letting it wrap the response in markdown fences or natural language.

Here’s the broken pattern:

BrokenFixed
The model is told to “return JSON” in plain English.The task uses an explicit schema and strict formatting instructions.
import { Agent, Task, Crew } from "crewai";

const agent = new Agent({
  role: "Analyst",
  goal: "Return customer risk data as JSON",
  backstory: "You analyze banking records.",
});

const task = new Task({
  description: `
    Analyze the customer and return JSON with fields:
    name, riskScore, flags.
    Return only JSON.
  `,
  agent,
});

const crew = new Crew({
  agents: [agent],
  tasks: [task],
});

The problem here is that “Return only JSON” is not enough. The model may still produce:

{
  "name": "Jane Doe",
  "riskScore": 72,
  "flags": ["high transfer volume"]
}

or this:

Sure — here's the JSON:
```json
{ ... }

Both can break parsing depending on how your TypeScript wrapper consumes `TaskOutput`.

Here’s the fixed pattern:

| Broken | Fixed |
|---|---|
| Free-form prompt expecting JSON. | Explicit structured output contract. |

```ts
import { z } from "zod";
import { Agent, Task, Crew } from "crewai";

const RiskSchema = z.object({
  name: z.string(),
  riskScore: z.number(),
  flags: z.array(z.string()),
});

const agent = new Agent({
  role: "Analyst",
  goal: "Return customer risk data in strict JSON",
  backstory: "You analyze banking records.",
});

const task = new Task({
  description: `
    Analyze the customer and return data matching this schema:
    name (string), riskScore (number), flags (string[]).
    Do not include markdown fences or commentary.
  `,
  agent,
  outputJsonSchema: RiskSchema, // use your CrewAI TS API's structured output option
});

If your CrewAI TypeScript version doesn’t expose outputJsonSchema, use whatever structured-output mechanism it provides, but keep the same rule: schema first, prompt second.

Other Possible Causes

1) Tool returns invalid JSON

A tool can be perfectly callable and still return broken payloads.

// Broken
async function getCustomerData() {
  return '{name:"Jane",riskScore:72}'; // invalid JSON
}

// Fixed
async function getCustomerData() {
  return JSON.stringify({ name: "Jane", riskScore: 72 });
}

If the tool result gets parsed by CrewAI, one missing quote is enough to trigger CrewAIError: JSON parsing error.

2) Model adds markdown fences

This is common with GPT-style outputs.

// Broken output from agent:
// ```json
// {"name":"Jane","riskScore":72,"flags":[]}
// ```

// Fixed instruction:
const task = new Task({
  description:
    "Return raw JSON only. No markdown fences, no prose, no code blocks.",
  agent,
});

If you see triple backticks in logs, that’s your culprit.

3) Schema mismatch between prompt and parser

Your prompt says one thing; your parser expects another.

// Broken expectation
type RiskReport = {
  customerName: string;
};

// Model returns:
{ "name": "Jane" }

Fix it by aligning field names exactly.

type RiskReport = {
  name: string;
};

4) Non-serializable values in task metadata

If you pass functions, Dates, or circular objects into config that later gets stringified, parsing can fail upstream.

// Broken
const task = new Task({
  description: "Analyze data",
  metadata: {
    startedAt: new Date(), // may serialize unexpectedly depending on implementation
    handler: () => {},
  },
});

Use plain serializable values:

const task = new Task({
  description: "Analyze data",
  metadata: {
    startedAtISO: new Date().toISOString(),
    handlerName: "customer-risk-handler",
  },
});

How to Debug It

  1. Print the raw model output before parsing

    • Log exactly what CrewAI received.
    • Look for markdown fences, extra text, trailing commas, or single quotes.
  2. Check whether the failure happens in the agent or tool layer

    • If it fails after a tool call, inspect the tool return value.
    • If it fails before tools run, inspect the prompt and response format.
  3. Validate against your schema locally

    • Take the raw string and run JSON.parse() on it.
    • Then validate with Zod or your runtime schema.
import { z } from "zod";

const RiskSchema = z.object({
  name: z.string(),
  riskScore: z.number(),
});

const raw = process.env.RAW_OUTPUT ?? "";

const parsed = JSON.parse(raw);
RiskSchema.parse(parsed);
  1. Strip formatting artifacts
    • If you see fenced code blocks in logs, remove them before parsing.
    • This should be a temporary diagnostic step only; fix the prompt afterward.

Prevention

  • Use strict schemas for every structured task.
  • Tell agents to return raw JSON only, with no markdown fences or commentary.
  • Keep tool outputs serializable and deterministic.
  • Add a local validation step with JSON.parse() plus Zod before handing data to downstream systems.

If you’re building regulated workflows for banking or insurance, treat every LLM response as untrusted input until validated. That’s how you avoid turning a simple formatting issue into a production incident.


Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides