mastra-ai/mastra · error · FileReadRequiredError

EREAD_REQUIRED

EREAD_REQUIRED

Error message

${reason}

What it means

To keep the model's context accurate, a read-tracker records the last-read time of files. If a write tool targets a file whose content changed on disk after the agent last read it (or was never read), `readTracker.needsReRead` returns a reason and the tool throws `FileReadRequiredError` with that reason. This forces a fresh read before overwriting, preventing lost-update bugs.

Source

Thrown at packages/core/src/workspace/tools/tools.ts:297

          enrichedContext = { ...enrichedContext, __expectedMtime: record.modifiedAtRead };
        }

        try {
          const stat = await fs.stat(input.path);

          // Policy gate: require the agent to have read the file first.
          // Only evaluate when explicitly configured (opt-in policy).
          // Safe default true = fail-closed if a dynamic function throws.
          if (config.requireReadBeforeWrite !== undefined) {
            const shouldRequireRead = await resolveDynamicValue(
              config.requireReadBeforeWrite,
              { args: input, requestContext: enrichedContext.requestContext ?? {}, workspace: effectiveWorkspace },
              true,
            );
            if (shouldRequireRead) {
              const check = readTracker.needsReRead(input.path, stat.modifiedAt);
              if (check.needsReRead) {
                throw new FileReadRequiredError(input.path, check.reason!);
              }
            }
          }
        } catch (error) {
          if (!(error instanceof FileNotFoundError)) {
            throw error;
          }
          // Missing file: if a read record exists the expectedMtime is
          // already attached, so downstream writeFile can treat this as
          // stale. Otherwise it's a genuinely new file.
        }
      }

      const result = await tool.execute(input, enrichedContext);

      // Post-execution: track reads / clear write records
      if (mode === 'read' && fs) {
        try {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Call the readFile tool on the path first, then retry the write so the tracker records a fresh read
  2. If the modification is expected/external, re-read and re-apply changes based on current content
  3. Structure agent workflows as read-then-write per file instead of blind writes

Example fix

// before
await writeFile({ path: 'config.ts', content: newContent }); // EREAD_REQUIRED

// after
const current = await readFile({ path: 'config.ts' });
await writeFile({ path: 'config.ts', content: merge(current, newContent) });
Defensive patterns

Strategy: retry

Try / catch

try {
  return await writeTool.execute({ path, content }, ctx);
} catch (err) {
  if (err?.code === 'EREAD_REQUIRED') {
    const current = await readFileTool.execute({ path }, ctx);
    return await writeTool.execute({ path, content: mergeWithCurrent(current, content) }, ctx);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a write/mutate tool on `input.path` where the file's `stat.modifiedAt` is newer than the tracked read time, or where the file was never read in this session (`needsReRead` true with a reason such as 'file not read' or 'modified since last read').

Common situations: External processes (linter/formatter, git checkout, another agent) modify a file between the agent's read and write; agent session resumes with stale read history; user edits a file while the agent works on it.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/6f8091e9ff8accaf. Report an issue: GitHub.