{"record":{"id":"1dab05ac31bef4f9","repo":"mastra-ai/mastra","slug":"cannot-save-course-state-user-is-not-registered","errorCode":null,"errorMessage":"Cannot save course state: User is not registered","messagePattern":"Cannot save course state: User is not registered","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp-docs-server/src/tools/course.ts","lineNumber":290,"sourceCode":"    headers: {\n      'Content-Type': 'application/json',\n      'x-mastra-course-key': creds.key,\n    },\n    body: JSON.stringify({\n      id: creds.deviceId,\n      state: state,\n    }),\n  });\n\n  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  }","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/mcp-docs-server/src/tools/course.ts#L272-L308","documentation":"saveCourseState requires a deviceId; a null deviceId means the user is not registered. It throws this error before writing any state, treating unregistered use of state-mutating course tools as an error condition.","triggerScenarios":"Calling startMastraCourse, startMastraCourseLesson, or nextMastraCourseStep on an unregistered device — deviceId resolved as null because registration never happened or credentials are absent.","commonSituations":"First run on a new machine/container without registering; credentials wiped; tests or agents invoking state-mutating course tools without the registration prerequisite.","solutions":["Complete device registration first, then retry the course tool call.","Use the read-only course tools (course state/content reads) if registration is not possible.","Check getDeviceCredentials() output before calling state-mutating tools to confirm a deviceId exists."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!deviceId) {\n  // do not call state-mutating course tools; register first or use read-only tools\n  throw new Error('Register the device before saving course state');\n}\nawait saveCourseState(state, deviceId);","typeGuard":"function isRegistered(deviceId: string | null): deviceId is string {\n  return typeof deviceId === 'string' && deviceId.length > 0;\n}","tryCatchPattern":"try {\n  await saveCourseState(state, deviceId);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('User is not registered')) {\n    // run device registration, then retry\n  } else throw e;\n}","preventionTips":["Gate all state-mutating course tool calls behind a registered-device check.","Register devices during onboarding/environment setup.","Fall back to read-only course tools when registration is unavailable."],"tags":["auth","registration","precondition"],"backgroundTag":"missing-credentials","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}