{"record":{"id":"b1b9b67edac3160e","repo":"mastra-ai/mastra","slug":"device-credentials-not-found-b1b9b6","errorCode":null,"errorMessage":"Device credentials not found","messagePattern":"Device credentials not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/mcp-docs-server/src/tools/course.ts","lineNumber":300,"sourceCode":"  if (!response.ok) {\n    throw new Error(`Course state update failed with status ${response.status}: ${response.statusText}`);\n  }\n}\n\nasync function saveCourseState(state: CourseState, deviceId: string | null): Promise<void> {\n  // If no device ID, the user isn't registered - this is an error condition\n  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  }","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/mcp-docs-server/src/tools/course.ts#L282-L318","documentation":"Inside saveCourseState's server-sync block, getDeviceCredentials() returned null and the code throws 'Device credentials not found'. However, this throw is inside a try/catch that intentionally swallows sync errors, so in practice it is caught and ignored — the local save already succeeded.","triggerScenarios":"Credentials disappearing between the outer deviceId check and the sync step, or getDeviceCredentials() returning null during the sync attempt inside saveCourseState.","commonSituations":"Credentials file removed concurrently; race between registration state checks; mostly observed when debugging sync behavior since the error is swallowed silently.","solutions":["Re-register the device so credentials are available for server sync.","Ignore it if local state saving is sufficient — the error is deliberately swallowed and sync is best-effort.","Add logging in the catch block if you need visibility into skipped syncs."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"const creds = await getDeviceCredentials();\nif (!creds) {\n  console.warn('Server sync skipped: no device credentials; relying on local state save only.');\n}","typeGuard":"function hasDeviceCredentials(c: unknown): c is { deviceId: string } {\n  return typeof c === 'object' && c !== null && typeof (c as { deviceId?: unknown }).deviceId === 'string';\n}","tryCatchPattern":"try {\n  await saveCourseState(state, deviceId);\n} catch {\n  // sync errors (including missing credentials) are swallowed by the library; local save already succeeded\n}","preventionTips":["Remember local state saving is primary; server sync failure here is non-fatal.","Re-register the device if you need server sync to actually work.","Add your own logging around saveCourseState since the library swallows sync errors."],"tags":["auth","credentials","sync","swallowed-error"],"backgroundTag":"missing-credentials","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}