{"record":{"id":"5094579d3f0e095a","repo":"mastra-ai/mastra","slug":"course-state-update-failed-with-status-response","errorCode":null,"errorMessage":"Course state update failed with status ${response.status}: ${response.statusText}","messagePattern":"Course state update failed with status (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp-docs-server/src/tools/course.ts","lineNumber":283,"sourceCode":"  const creds = await getDeviceCredentials();\n  if (!creds) {\n    throw new Error('Device credentials not found.');\n  }\n\n  const response = await fetch('https://mastra.ai/api/course/update', {\n    method: 'POST',\n    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);","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/mcp-docs-server/src/tools/course.ts#L265-L301","documentation":"The POST to https://mastra.ai/api/course/update returned a non-OK HTTP status. The server rejected the state sync (auth failure, validation error, server error), and the library surfaces the status code and status text.","triggerScenarios":"fetch('https://mastra.ai/api/course/update') responds with 4xx/5xx: invalid or expired device credentials, malformed state payload, network-level proxy interception, or mastra.ai API outage.","commonSituations":"Expired/revoked device ID; corporate proxy returning 403/502; mastra.ai API downtime; course state schema changed server-side after a client update.","solutions":["Check the HTTP status: 401/403 → re-register the device to get fresh credentials; 5xx → retry later.","Verify network/proxy access to mastra.ai from the machine.","Note that the local save may still have succeeded — check local state before assuming data loss, then retry the sync."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const res = await fetch('https://mastra.ai/api/course/update', { method: 'HEAD' }).catch(() => null);\nif (!res || !res.ok) {\n  // mastra.ai unreachable or degraded — defer sync instead of calling saveCourseState\n}","typeGuard":null,"tryCatchPattern":"try {\n  await saveCourseState(state, deviceId);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Course state update failed with status')) {\n    const status = Number(e.message.match(/status (\\d+)/)?.[1]);\n    if (status >= 500 || status === 429) { /* retry with backoff */ }\n    else if (status === 401 || status === 403) { /* re-register device */ }\n  } else throw e;\n}","preventionTips":["Retry 5xx/429 responses with exponential backoff; re-authenticate on 401/403.","Keep local state as the source of truth; treat server sync as best-effort.","Monitor mastra.ai status before bulk sync operations."],"tags":["network","http","api"],"backgroundTag":"http-request-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}