mastra-ai/mastra · error · Error

Failed to load course state: ${error}

Error message

Failed to load course state: ${error}

What it means

loadCourseState reads and JSON.parses the locally stored course state file. If reading or parsing fails (corrupt JSON, unreadable file), it throws 'Failed to load course state: <error>'. A missing file is NOT an error — it returns null.

Source

Thrown at packages/mcp-docs-server/src/tools/course.ts:332

  // Ensure the directory exists
  if (!existsSync(stateDirPath)) {
    mkdirSync(stateDirPath, { recursive: true });
  }

  return path.join(stateDirPath, 'state.json');
}

async function loadCourseState(): Promise<CourseState | null> {
  const statePath = await getCourseStatePath();

  try {
    if (existsSync(statePath)) {
      const stateData = await fs.readFile(statePath, 'utf-8');
      return JSON.parse(stateData) as CourseState;
    }
  } catch (error) {
    throw new Error(`Failed to load course state: ${error}`);
  }

  return null;
}

async function scanCourseContent(): Promise<CourseState> {
  // Scan the course directory to build a fresh state
  const lessonDirs = await fs.readdir(courseDir);

  const lessons = await Promise.all(
    lessonDirs
      .filter(dir => !dir.startsWith('.')) // Skip hidden directories
      .sort((a, b) => a.localeCompare(b))
      .map(async lessonDir => {
        const lessonPath = path.join(courseDir, lessonDir);
        const lessonStats = await fs.stat(lessonPath);

        if (!lessonStats.isDirectory()) return null;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Delete or fix the corrupted state file at ~/.cache/mastra/course so it contains valid JSON (or let it be recreated).
  2. Validate the JSON with a parser and repair the fields you need.
  3. Check file permissions if the message wraps an EACCES read error.

Example fix

// before
// corrupted state file read directly
const state = await loadCourseState();
// after
// reset corrupt local state
await fs.rm(statePath, { force: true });
const state = await loadCourseState(); // returns null, then re-initialized
Defensive patterns

Strategy: try-catch

Validate before calling

import { existsSync } from 'node:fs';
import { readFileSync } from 'node:fs';
if (existsSync(statePath)) {
  try { JSON.parse(readFileSync(statePath, 'utf-8')); } catch {
    // corrupt state file — delete or repair before calling loadCourseState
  }
}

Type guard

function isValidCourseState(v: unknown): v is CourseState {
  return typeof v === 'object' && v !== null;
}

Try / catch

let state: CourseState | null = null;
try {
  state = await loadCourseState();
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Failed to load course state:')) {
    state = null; // reset to fresh state; optionally delete the corrupt file
  } else throw e;
}

Prevention

When it happens

Trigger: fs.readFile succeeds but the file contains invalid JSON (manual edits, truncated write from a previous crash), or readFile itself fails on an existing but unreadable statePath.

Common situations: State file corrupted by a prior ENOSPC-interrupted write; user hand-edited ~/.cache/mastra/course state and broke JSON; permissions changed on the cache file.

Related errors


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