mastra-ai/mastra · error · Error

Failed to save course state: ${error}

Error message

Failed to save course state: ${error}

What it means

The outer try/catch in saveCourseState wraps the local filesystem write of the course state file; if fs.writeFile (or getCourseStatePath) fails, the error is rethrown as 'Failed to save course state: <underlying error>'. This means the user's course progress could not be persisted locally.

Source

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

  if (!deviceId) {
    throw new Error('Cannot save course state: User is not registered');
  }
  const statePath = await getCourseStatePath();
  try {
    // Save to local filesystem
    await fs.writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');
    // Sync with server
    try {
      // Use getDeviceCredentials to ensure we have the key
      const creds = await getDeviceCredentials();
      if (!creds) throw new Error('Device credentials not found');
      await updateCourseStateOnServer(creds.deviceId, state);
    } catch {
      // Silently continue if server sync fails
      // Local save is still successful
    }
  } catch (error) {
    throw new Error(`Failed to save course state: ${error}`);
  }
}

// Get the path to the course state file
async function getCourseStatePath(): Promise<string> {
  const stateDirPath = path.join(os.homedir(), '.cache', 'mastra', 'course');

  // 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();

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure ~/.cache/mastra/course exists and is writable (mkdir -p, fix permissions).
  2. Check disk space and that HOME points to a writable directory.
  3. Inspect the wrapped underlying error in the message for the exact fs failure code (EACCES, ENOSPC, ENOENT).

Example fix

// before
// assuming the cache dir exists
await fs.writeFile(statePath, data, 'utf-8');
// after
await fs.mkdir(path.dirname(statePath), { recursive: true });
await fs.writeFile(statePath, data, 'utf-8');
Defensive patterns

Strategy: try-catch

Validate before calling

import { accessSync, constants, mkdirSync } from 'node:fs';
import path from 'node:path';
import os from 'node:os';
const stateDir = path.join(os.homedir(), '.cache', 'mastra', 'course');
mkdirSync(stateDir, { recursive: true });
accessSync(stateDir, constants.W_OK); // throws early if not writable

Try / catch

try {
  await saveCourseState(state, deviceId);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Failed to save course state:')) {
    // inspect wrapped fs error (EACCES/ENOSPC/ENOENT) and fix dir perms/disk/HOME
  } else throw e;
}

Prevention

When it happens

Trigger: fs.writeFile to ~/.cache/mastra/course/<state file> rejects: the cache directory does not exist and was not created, disk full, permission denied on ~/.cache, or a read-only filesystem.

Common situations: Home directory not writable in containers/CI (HOME set to a read-only path); deleted ~/.cache/mastra mid-session; ENOSPC on full disks; sandboxed environments blocking writes to $HOME.

Related errors


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