# Source: https://docs.convex.dev/error.md

# Errors and Warnings

This page explains specific errors thrown by Convex.

See [Error Handling](/functions/error-handling/.md) to learn about handling errors in general.

## Write conflict: Optimistic concurrency control[​](#1 "Direct link to Write conflict: Optimistic concurrency control")

This system error is thrown when a mutation repeatedly fails due to conflicting changes from parallel mutation executions.

### Example A[​](#example-a "Direct link to Example A")

A mutation `updateCounter` always updates the same document:

```
export const updateCounter = mutation({
  args: {},
  handler: async (ctx) => {
    const doc = await ctx.db.get("counts", process.env.COUNTER_ID);
    await ctx.db.patch("counts", doc._id, { value: doc.value + 1 });
  },
});
```

If this mutation is called many times per second, many of its executions will conflict with each other. Convex internally does several retries to mitigate this concern, but if the mutation is called more rapidly than Convex can execute it, some of the invocations will eventually throw this error:

> failure `updateCounter`
>
> Documents read from or written to the table "counters" changed while this mutation was being run and on every subsequent retry. Another call to this mutation changed the document with ID "123456789101112".

The error message will note the table name, which mutation caused the conflict (in this example its another call to the same mutation), and one document ID which was part of the conflicting change.

### Example B[​](#example-b "Direct link to Example B")

Mutation `writeCount` depends on the entire `tasks` table:

```
export const writeCount = mutation({
  args: {
    target: v.id("counts"),
  },
  handler: async (ctx, args) => {
    const tasks = await ctx.db.query("tasks").collect();
    await ctx.db.patch("tasks", args.target, { value: tasks });
  },
});

export const addTask = mutation({
  args: {
    text: v.string(),
  },
  handler: async (ctx, args) => {
    await ctx.db.insert("tasks", { text: args.text });
  },
});
```

If the mutation `writeCount` is called at the same time as many calls to `addTask` are made, either of the mutations can fail with this error. This is because any change to the `"tasks"` table will conflict with the `writeCount` mutation:

> failure `writeCount`
>
> Documents read from or written to the table "tasks" changed while this mutation was being run and on every subsequent retry. A call to "addTask" changed the document with ID "123456789101112".

### Remediation[​](#remediation "Direct link to Remediation")

To fix this issue:

1. Make sure that your mutations only read the data they need. Consider reducing the amount of data read by using indexed queries with [selective index range expressions](https://docs.convex.dev/database/indexes/).
2. Make sure you are not calling a mutation an unexpected number of times, perhaps from an action inside a loop.
3. Design your data model such that it doesn't require making many writes to the same document.

### Resources[​](#resources "Direct link to Resources")

* Learn more about [optimistic concurrency control](/database/advanced/occ.md).
* See this [Stack post](https://stack.convex.dev/waitlist) for an example of designing an app to avoid mutation conflicts.

### Related Components[​](#related-components "Direct link to Related Components")

[Convex Component](https://www.convex.dev/components/workpool)

### [Workpool](https://www.convex.dev/components/workpool)

[Workpool give critical tasks priority by organizing async operations into separate, customizable queues.](https://www.convex.dev/components/workpool)

[Convex Component](https://www.convex.dev/components/sharded-counter)

### [Sharded Counter](https://www.convex.dev/components/sharded-counter)

[High-throughput counter enables denormalized counts without write conflicts by spreading writes over multiple documents.](https://www.convex.dev/components/sharded-counter)

[Convex Component](https://www.convex.dev/components/action-cache)

### [Action Cache](https://www.convex.dev/components/action-cache)

[Cache frequently run actions. By leveraging the \`force\` parameter to keep the cache populated, you can ensure that the cache is always up to date and avoid data races.](https://www.convex.dev/components/action-cache)

## Undefined validator[​](#undefined-validator "Direct link to Undefined validator")

This error occurs when a validator passed to a Convex function definition or schema is `undefined`. This most commonly happens due to circular imports (also known as import cycles) in TypeScript.

### Example[​](#example "Direct link to Example")

You have two files that import from each other:

convex/validators.ts

TS

```
import { v } from "convex/values";
import { someUtility } from "./functions";

export const myValidator = v.object({
  name: v.string(),
});

// Uses someUtility somewhere...
```

convex/functions.ts

TS

```
import { mutation } from "./_generated/server";
// Both functions.ts and validators.ts import from each other.
import { myValidator } from "./validators";

export function someUtility() {
  // ...
}

export const myMutation = mutation({
  args: {
    data: myValidator, // <-- May be undefined due to import cycle
  },
  handler: async (ctx, args) => {
    // ...
  },
});
```

When `functions.ts` is loaded, it imports from `validators.ts`, which in turn tries to import from `functions.ts`. Since `functions.ts` hasn't finished the `import` statement yet, `myValidator` is still `undefined`, causing the `mutation` builder to throw an error.

Note: the value may be defined at runtime if you try to log it. This is only a quirk of TypeScript’s import time behavior.

### Cycles involving `schema.ts`[​](#cycles-involving-schemats "Direct link to cycles-involving-schemats")

A common way to accidentally introduce this kind of cycle is through your `schema.ts` file. Larger apps often define validators or whole tables in other files and import them into `schema.ts`.

If these files import from `schema.ts` or depend on files that do, you have a cycle.

```
schema.ts → validators.ts → someFile.ts → schema.ts
```

To break the cycle, define validators in "pure" files that have minimal dependencies, and import them into the places they are needed.

### Investigate circular imports[​](#investigate-circular-imports "Direct link to Investigate circular imports")

If you suspect a circular import but aren't sure where it is, tools like [madge](https://github.com/pahen/madge) can help you visualize your import graph and list cycles:

```
npx madge convex/ --extensions ts --exclude api.d.ts --circular
```

We exclude `api.d.ts` here because type-only imports are generally safe.