{"record":{"id":"2d6bc6842274b088","repo":"mastra-ai/mastra","slug":"failed-to-save-course-state-error","errorCode":null,"errorMessage":"Failed to save course state: ${error}","messagePattern":"Failed to save course state: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp-docs-server/src/tools/course.ts","lineNumber":307,"sourceCode":"  if (!deviceId) {\n    throw new Error('Cannot save course state: User is not registered');\n  }\n  const statePath = await getCourseStatePath();\n  try {\n    // Save to local filesystem\n    await fs.writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');\n    // Sync with server\n    try {\n      // Use getDeviceCredentials to ensure we have the key\n      const creds = await getDeviceCredentials();\n      if (!creds) throw new Error('Device credentials not found');\n      await updateCourseStateOnServer(creds.deviceId, state);\n    } catch {\n      // Silently continue if server sync fails\n      // Local save is still successful\n    }\n  } catch (error) {\n    throw new Error(`Failed to save course state: ${error}`);\n  }\n}\n\n// Get the path to the course state file\nasync function getCourseStatePath(): Promise<string> {\n  const stateDirPath = path.join(os.homedir(), '.cache', 'mastra', 'course');\n\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","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/mcp-docs-server/src/tools/course.ts#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure ~/.cache/mastra/course exists and is writable (mkdir -p, fix permissions).","Check disk space and that HOME points to a writable directory.","Inspect the wrapped underlying error in the message for the exact fs failure code (EACCES, ENOSPC, ENOENT)."],"exampleFix":"// before\n// assuming the cache dir exists\nawait fs.writeFile(statePath, data, 'utf-8');\n// after\nawait fs.mkdir(path.dirname(statePath), { recursive: true });\nawait fs.writeFile(statePath, data, 'utf-8');","handlingStrategy":"try-catch","validationCode":"import { accessSync, constants, mkdirSync } from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nconst stateDir = path.join(os.homedir(), '.cache', 'mastra', 'course');\nmkdirSync(stateDir, { recursive: true });\naccessSync(stateDir, constants.W_OK); // throws early if not writable","typeGuard":null,"tryCatchPattern":"try {\n  await saveCourseState(state, deviceId);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Failed to save course state:')) {\n    // inspect wrapped fs error (EACCES/ENOSPC/ENOENT) and fix dir perms/disk/HOME\n  } else throw e;\n}","preventionTips":["Ensure ~/.cache/mastra/course exists and is writable before running course tools.","Verify HOME points to a writable directory in containers and CI.","Watch disk space; ENOSPC produces this wrapped error."],"tags":["filesystem","persistence","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}