mastra-ai/mastra · warning · Error

Device credentials not found

Error message

Device credentials not found

What it means

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.

Source

Thrown at packages/mcp-docs-server/src/tools/course.ts:300

  if (!response.ok) {
    throw new Error(`Course state update failed with status ${response.status}: ${response.statusText}`);
  }
}

async function saveCourseState(state: CourseState, deviceId: string | null): Promise<void> {
  // If no device ID, the user isn't registered - this is an error condition
  if (!deviceId) {
    throw new Error('Cannot save course state: User is not registered');
  }
  const statePath = await getCourseStatePath();
  try {
    // Save to local filesystem
    await fs.writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');
    // Sync with server
    try {
      // Use getDeviceCredentials to ensure we have the key
      const creds = await getDeviceCredentials();
      if (!creds) throw new Error('Device credentials not found');
      await updateCourseStateOnServer(creds.deviceId, state);
    } catch {
      // Silently continue if server sync fails
      // Local save is still successful
    }
  } catch (error) {
    throw new Error(`Failed to save course state: ${error}`);
  }
}

// Get the path to the course state file
async function getCourseStatePath(): Promise<string> {
  const stateDirPath = path.join(os.homedir(), '.cache', 'mastra', 'course');

  // Ensure the directory exists
  if (!existsSync(stateDirPath)) {
    mkdirSync(stateDirPath, { recursive: true });
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Re-register the device so credentials are available for server sync.
  2. Ignore it if local state saving is sufficient — the error is deliberately swallowed and sync is best-effort.
  3. Add logging in the catch block if you need visibility into skipped syncs.
Defensive patterns

Strategy: fallback

Validate before calling

const creds = await getDeviceCredentials();
if (!creds) {
  console.warn('Server sync skipped: no device credentials; relying on local state save only.');
}

Type guard

function hasDeviceCredentials(c: unknown): c is { deviceId: string } {
  return typeof c === 'object' && c !== null && typeof (c as { deviceId?: unknown }).deviceId === 'string';
}

Try / catch

try {
  await saveCourseState(state, deviceId);
} catch {
  // sync errors (including missing credentials) are swallowed by the library; local save already succeeded
}

Prevention

When it happens

Trigger: Credentials disappearing between the outer deviceId check and the sync step, or getDeviceCredentials() returning null during the sync attempt inside saveCourseState.

Common situations: Credentials file removed concurrently; race between registration state checks; mostly observed when debugging sync behavior since the error is swallowed silently.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/b1b9b67edac3160e. Report an issue: GitHub.