{"record":{"id":"7d75841cc69796d1","repo":"mastra-ai/mastra","slug":"failed-to-load-course-state-error","errorCode":null,"errorMessage":"Failed to load course state: ${error}","messagePattern":"Failed to load course state: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp-docs-server/src/tools/course.ts","lineNumber":332,"sourceCode":"\n  // Ensure the directory exists\n  if (!existsSync(stateDirPath)) {\n    mkdirSync(stateDirPath, { recursive: true });\n  }\n\n  return path.join(stateDirPath, 'state.json');\n}\n\nasync function loadCourseState(): Promise<CourseState | null> {\n  const statePath = await getCourseStatePath();\n\n  try {\n    if (existsSync(statePath)) {\n      const stateData = await fs.readFile(statePath, 'utf-8');\n      return JSON.parse(stateData) as CourseState;\n    }\n  } catch (error) {\n    throw new Error(`Failed to load course state: ${error}`);\n  }\n\n  return null;\n}\n\nasync function scanCourseContent(): Promise<CourseState> {\n  // Scan the course directory to build a fresh state\n  const lessonDirs = await fs.readdir(courseDir);\n\n  const lessons = await Promise.all(\n    lessonDirs\n      .filter(dir => !dir.startsWith('.')) // Skip hidden directories\n      .sort((a, b) => a.localeCompare(b))\n      .map(async lessonDir => {\n        const lessonPath = path.join(courseDir, lessonDir);\n        const lessonStats = await fs.stat(lessonPath);\n\n        if (!lessonStats.isDirectory()) return null;","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/mcp-docs-server/src/tools/course.ts#L314-L350","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete or fix the corrupted state file at ~/.cache/mastra/course so it contains valid JSON (or let it be recreated).","Validate the JSON with a parser and repair the fields you need.","Check file permissions if the message wraps an EACCES read error."],"exampleFix":"// before\n// corrupted state file read directly\nconst state = await loadCourseState();\n// after\n// reset corrupt local state\nawait fs.rm(statePath, { force: true });\nconst state = await loadCourseState(); // returns null, then re-initialized","handlingStrategy":"try-catch","validationCode":"import { existsSync } from 'node:fs';\nimport { readFileSync } from 'node:fs';\nif (existsSync(statePath)) {\n  try { JSON.parse(readFileSync(statePath, 'utf-8')); } catch {\n    // corrupt state file — delete or repair before calling loadCourseState\n  }\n}","typeGuard":"function isValidCourseState(v: unknown): v is CourseState {\n  return typeof v === 'object' && v !== null;\n}","tryCatchPattern":"let state: CourseState | null = null;\ntry {\n  state = await loadCourseState();\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Failed to load course state:')) {\n    state = null; // reset to fresh state; optionally delete the corrupt file\n  } else throw e;\n}","preventionTips":["Treat null (missing file) as 'fresh state' — only catch-and-reset on parse errors.","Never hand-edit the course state JSON; if you do, validate it afterwards.","Write state atomically (tmp file + rename) to avoid truncated files on crash."],"tags":["filesystem","json","corruption"],"backgroundTag":"json-parse-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}