mastra-ai/mastra · error

Could not save the active model pack

Error message

Could not save the active model pack

What it means

When switching the model for a mode, the TUI first persists the new model pack id to thread settings, then reads it back to verify the write actually landed. If the round-trip read does not equal the pack id that was just written, switchCurrentModeModel throws 'Could not save the active model pack'. This is a write-verification failure indicating the settings backend silently dropped or failed to persist the value.

Source

Thrown at mastracode/tui/src/tui/commands/models.ts:93

      });
    }
    nextSettings.models.activeModelPackId = nextPackId;
    nextSettings.models.modeDefaults = modeModels;
  }

  let modeSettingSaved = false;
  let packSettingSaved = false;
  let globalSettingsWriteStarted = false;
  try {
    await ctx.state.session.thread.setSetting({ key: modeSettingKey, value: modelId });
    modeSettingSaved = true;
    const savedModeSetting = await ctx.state.session.thread.getSetting({ key: modeSettingKey });
    if (savedModeSetting !== modelId) throw new Error(`Could not save the ${modeId} mode model`);

    await ctx.state.session.thread.setSetting({ key: THREAD_ACTIVE_MODEL_PACK_ID_KEY, value: nextPackId });
    packSettingSaved = true;
    const savedPackSetting = await ctx.state.session.thread.getSetting({ key: THREAD_ACTIVE_MODEL_PACK_ID_KEY });
    if (savedPackSetting !== nextPackId) throw new Error('Could not save the active model pack');
    globalSettingsWriteStarted = true;
    saveSettings(nextSettings);
    await ctx.state.session.model.switch({ modelId, scope: 'global' });
  } catch (error) {
    if (globalSettingsWriteStarted) {
      try {
        saveSettings(settings);
      } catch {
        // Keep the original failure. The thread and active model still roll back below.
      }
    }

    const rollbacks: Array<Promise<unknown>> = [];
    if (packSettingSaved) {
      rollbacks.push(
        ctx.state.session.thread.setSetting({
          key: THREAD_ACTIVE_MODEL_PACK_ID_KEY,
          value: previousPackSetting,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check that the thread's storage/settings backend persists values synchronously and that getSetting reflects a prior setSetting
  2. Retry the model switch; transient races with another writer resolve on a second attempt
  3. Verify no other TUI session or process is concurrently writing THREAD_ACTIVE_MODEL_PACK_ID_KEY for the same thread
  4. Inspect the thread settings implementation for swallowed errors in setSetting
  5. Clear/recreate the thread state if its settings record is corrupted

Example fix

// before (custom storage adapter silently ignoring writes)
async setSetting({ key, value }) { this.pending[key] = value; }
// after
async setSetting({ key, value }) {
  this.pending[key] = value;
  await this.persist();
  const check = await this.getSetting({ key });
  if (check !== value) throw new Error(`Failed to persist setting ${key}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const stored = await ctx.state.session.thread.getSetting({ key: THREAD_ACTIVE_MODEL_PACK_ID_KEY });
if (stored !== undefined && typeof stored !== 'string') throw new Error('Thread settings backend returned unexpected value');

Type guard

function isPersistedSetting(v: unknown): v is string { return typeof v === 'string'; }

Try / catch

try {
  await switchCurrentModeModel(ctx, modeId, modelId, packId);
} catch (error) {
  if (error instanceof Error && error.message.includes('Could not save')) {
    // surface a retry / settings-backend diagnostics UI
  }
}

Prevention

When it happens

Trigger: Calling the mode-model switch command when thread.setSetting({ key: THREAD_ACTIVE_MODEL_PACK_ID_KEY, ... }) succeeds without throwing but the subsequent getSetting returns a different value or undefined — e.g. the thread storage adapter silently swallows writes, the thread was closed/recreated mid-switch, or a concurrent write overwrote the key between set and get.

Common situations: Storage backend misconfiguration (in-memory or ephemeral store that doesn't persist), corrupted thread state, race with another TUI session writing settings for the same thread, or a custom Thread/Settings implementation whose setSetting doesn't immediately reflect in getSetting.

Related errors


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