fix: prevent crash on macos 26.x beta during startup (#473)

defer apple intelligence availability check from app initialization
to when the user actually tries to use the feature. accessing
SystemLanguageModel.default during early app startup causes SIGABRT
on macOS 26.x (tahoe) beta.

- remove availability check from default_post_process_providers()
- always include apple intelligence provider on macos arm64
- add checkAppleIntelligenceAvailable command for lazy checking
- show error message if apple intelligence unavailable when selected
- clear error on any dropdown selection for better UX
This commit is contained in:
Viren Mohindra 2026-01-04 05:22:06 +05:30 committed by GitHub
parent f7e73fc602
commit d3a0281062
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 109 additions and 30 deletions

View file

@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to build'
description: "PR number to build"
required: true
type: string

View file

@ -115,3 +115,18 @@ pub fn open_app_data_dir(app: AppHandle) -> Result<(), String> {
Ok(())
}
/// Check if Apple Intelligence is available on this device.
/// Called by the frontend when the user selects Apple Intelligence provider.
#[specta::specta]
#[tauri::command]
pub fn check_apple_intelligence_available() -> bool {
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
crate::apple_intelligence::check_apple_intelligence_availability()
}
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
{
false
}
}

View file

@ -274,6 +274,7 @@ pub fn run() {
commands::open_recordings_folder,
commands::open_log_dir,
commands::open_app_data_dir,
commands::check_apple_intelligence_available,
commands::models::get_available_models,
commands::models::get_model_info,
commands::models::download_model,

View file

@ -97,6 +97,10 @@ pub struct PostProcessProvider {
pub id: String,
pub label: String,
pub base_url: String,
#[serde(default)]
pub allow_base_url_edit: bool,
#[serde(default)]
pub models_endpoint: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Type)]
@ -374,45 +378,63 @@ fn default_post_process_providers() -> Vec<PostProcessProvider> {
id: "openai".to_string(),
label: "OpenAI".to_string(),
base_url: "https://api.openai.com/v1".to_string(),
allow_base_url_edit: false,
models_endpoint: Some("/models".to_string()),
},
PostProcessProvider {
id: "openrouter".to_string(),
label: "OpenRouter".to_string(),
base_url: "https://openrouter.ai/api/v1".to_string(),
allow_base_url_edit: false,
models_endpoint: Some("/models".to_string()),
},
PostProcessProvider {
id: "anthropic".to_string(),
label: "Anthropic".to_string(),
base_url: "https://api.anthropic.com/v1".to_string(),
allow_base_url_edit: false,
models_endpoint: Some("/models".to_string()),
},
PostProcessProvider {
id: "groq".to_string(),
label: "Groq".to_string(),
base_url: "https://api.groq.com/openai/v1".to_string(),
allow_base_url_edit: false,
models_endpoint: Some("/models".to_string()),
},
PostProcessProvider {
id: "cerebras".to_string(),
label: "Cerebras".to_string(),
base_url: "https://api.cerebras.ai/v1".to_string(),
},
PostProcessProvider {
id: "custom".to_string(),
label: "Custom".to_string(),
base_url: "http://localhost:11434/v1".to_string(),
allow_base_url_edit: false,
models_endpoint: Some("/models".to_string()),
},
];
// Note: We always include Apple Intelligence on macOS ARM64 without checking availability
// at startup. The availability check is deferred to when the user actually tries to use it
// (in actions.rs). This prevents crashes on macOS 26.x beta where accessing
// SystemLanguageModel.default during early app initialization causes SIGABRT.
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
if crate::apple_intelligence::check_apple_intelligence_availability() {
providers.push(PostProcessProvider {
id: APPLE_INTELLIGENCE_PROVIDER_ID.to_string(),
label: "Apple Intelligence".to_string(),
base_url: "apple-intelligence://local".to_string(),
});
}
providers.push(PostProcessProvider {
id: APPLE_INTELLIGENCE_PROVIDER_ID.to_string(),
label: "Apple Intelligence".to_string(),
base_url: "apple-intelligence://local".to_string(),
allow_base_url_edit: false,
models_endpoint: None,
});
}
// Custom provider always comes last
providers.push(PostProcessProvider {
id: "custom".to_string(),
label: "Custom".to_string(),
base_url: "http://localhost:11434/v1".to_string(),
allow_base_url_edit: true,
models_endpoint: Some("/models".to_string()),
});
providers
}

View file

@ -343,6 +343,13 @@ async openAppDataDir() : Promise<Result<null, string>> {
else return { status: "error", error: e as any };
}
},
/**
* Check if Apple Intelligence is available on this device.
* Called by the frontend when the user selects Apple Intelligence provider.
*/
async checkAppleIntelligenceAvailable() : Promise<boolean> {
return await TAURI_INVOKE("check_apple_intelligence_available");
},
async getAvailableModels() : Promise<Result<ModelInfo[], string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_available_models") };
@ -635,7 +642,7 @@ export type ModelLoadStatus = { is_loaded: boolean; current_model: string | null
export type ModelUnloadTimeout = "never" | "immediately" | "min_2" | "min_5" | "min_10" | "min_15" | "hour_1" | "sec_5"
export type OverlayPosition = "none" | "top" | "bottom"
export type PasteMethod = "ctrl_v" | "direct" | "none" | "shift_insert" | "ctrl_shift_v"
export type PostProcessProvider = { id: string; label: string; base_url: string }
export type PostProcessProvider = { id: string; label: string; base_url: string; allow_base_url_edit?: boolean; models_endpoint?: string | null }
export type RecordingRetentionPeriod = "never" | "preserve_limit" | "days_3" | "weeks_2" | "months_3"
export type ShortcutBinding = { id: string; name: string; description: string; default_binding: string; current_binding: string }
export type SoundTheme = "marimba" | "pop" | "custom"

View file

@ -1,7 +1,6 @@
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useMemo, useState } from "react";
import { useSettings } from "../../../hooks/useSettings";
import { useSettingsStore } from "../../../stores/settingsStore";
import type { PostProcessProvider } from "@/bindings";
import { commands, type PostProcessProvider } from "@/bindings";
import type { ModelOption } from "./types";
import type { DropdownOption } from "../../ui/Dropdown";
@ -12,6 +11,7 @@ type PostProcessProviderState = {
selectedProvider: PostProcessProvider | undefined;
isCustomProvider: boolean;
isAppleProvider: boolean;
appleIntelligenceUnavailable: boolean;
baseUrl: string;
handleBaseUrlChange: (value: string) => void;
isBaseUrlUpdating: boolean;
@ -60,6 +60,8 @@ export const usePostProcessProviderState = (): PostProcessProviderState => {
}, [providers, selectedProviderId]);
const isAppleProvider = selectedProvider?.id === APPLE_PROVIDER_ID;
const [appleIntelligenceUnavailable, setAppleIntelligenceUnavailable] =
useState(false);
// Use settings directly as single source of truth
const baseUrl = selectedProvider?.base_url ?? "";
@ -74,10 +76,23 @@ export const usePostProcessProviderState = (): PostProcessProviderState => {
}, [providers]);
const handleProviderSelect = useCallback(
(providerId: string) => {
if (providerId !== selectedProviderId) {
void setPostProcessProvider(providerId);
async (providerId: string) => {
// Clear error state on any selection attempt (allows dismissing the error)
setAppleIntelligenceUnavailable(false);
if (providerId === selectedProviderId) return;
// Check Apple Intelligence availability before selecting
if (providerId === APPLE_PROVIDER_ID) {
const available = await commands.checkAppleIntelligenceAvailable();
if (!available) {
setAppleIntelligenceUnavailable(true);
// Don't return - still set the provider so dropdown shows the selection
// The backend gracefully handles unavailable Apple Intelligence
}
}
void setPostProcessProvider(providerId);
},
[selectedProviderId, setPostProcessProvider],
);
@ -182,6 +197,7 @@ export const usePostProcessProviderState = (): PostProcessProviderState => {
selectedProvider,
isCustomProvider,
isAppleProvider,
appleIntelligenceUnavailable,
baseUrl,
handleBaseUrlChange,
isBaseUrlUpdating,

View file

@ -56,6 +56,14 @@ const PostProcessingSettingsApiComponent: React.FC = () => {
</div>
</SettingContainer>
{state.appleIntelligenceUnavailable ? (
<div className="p-3 bg-red-500/10 border border-red-500/50">
<p className="text-sm text-red-500">
{t("settings.postProcessing.api.appleIntelligence.unavailable")}
</p>
</div>
) : null}
{state.isAppleProvider ? (
<SettingContainer
title={t("settings.postProcessing.api.appleIntelligence.title")}

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Läuft vollständig auf dem Gerät. Kein API-Schlüssel oder Netzwerkzugriff erforderlich.",
"requirements": "Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer. Apple Intelligence muss in den Systemeinstellungen aktiviert sein."
"requirements": "Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer. Apple Intelligence muss in den Systemeinstellungen aktiviert sein.",
"unavailable": "Apple Intelligence ist auf diesem Gerät nicht verfügbar. Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer und aktiviertem Apple Intelligence in den Systemeinstellungen."
},
"baseUrl": {
"title": "Basis-URL",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Runs fully on-device. No API key or network access is required.",
"requirements": "Requires an Apple Silicon Mac running macOS Tahoe (26.0) or later. Apple Intelligence must be enabled in System Settings."
"requirements": "Requires an Apple Silicon Mac running macOS Tahoe (26.0) or later. Apple Intelligence must be enabled in System Settings.",
"unavailable": "Apple Intelligence is not available on this device. Requires an Apple Silicon Mac running macOS Tahoe (26.0) or later with Apple Intelligence enabled in System Settings."
},
"baseUrl": {
"title": "Base URL",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Se ejecuta completamente en el dispositivo. No se requiere clave API ni acceso a la red.",
"requirements": "Requiere un Mac con Apple Silicon ejecutando macOS Tahoe (26.0) o posterior. Apple Intelligence debe estar habilitado en Ajustes del Sistema."
"requirements": "Requiere un Mac con Apple Silicon ejecutando macOS Tahoe (26.0) o posterior. Apple Intelligence debe estar habilitado en Ajustes del Sistema.",
"unavailable": "Apple Intelligence no está disponible en este dispositivo. Requiere un Mac con Apple Silicon ejecutando macOS Tahoe (26.0) o posterior con Apple Intelligence habilitado en Ajustes del Sistema."
},
"baseUrl": {
"title": "URL Base",

View file

@ -216,7 +216,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Fonctionne entièrement sur l'appareil. Aucune clé API ni accès réseau n'est requis.",
"requirements": "Nécessite un Mac Apple Silicon exécutant macOS Tahoe (26.0) ou une version ultérieure. Apple Intelligence doit être activé dans les Préférences Système."
"requirements": "Nécessite un Mac Apple Silicon exécutant macOS Tahoe (26.0) ou une version ultérieure. Apple Intelligence doit être activé dans les Préférences Système.",
"unavailable": "Apple Intelligence n'est pas disponible sur cet appareil. Nécessite un Mac Apple Silicon exécutant macOS Tahoe (26.0) ou une version ultérieure avec Apple Intelligence activé dans les Préférences Système."
},
"baseUrl": {
"title": "URL de base",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Si esegue completamente in locale. Non è necessaria una chiave API né l'accesso alla rete.",
"requirements": "Necessita di un Mac con Apple Silicon e macOS Tahoe (26.0) o successivi. Apple Intelligence deve essere abilitata nelle Impostazioni di Sistema."
"requirements": "Necessita di un Mac con Apple Silicon e macOS Tahoe (26.0) o successivi. Apple Intelligence deve essere abilitata nelle Impostazioni di Sistema.",
"unavailable": "Apple Intelligence non è disponibile su questo dispositivo. Necessita di un Mac con Apple Silicon e macOS Tahoe (26.0) o successivi con Apple Intelligence abilitata nelle Impostazioni di Sistema."
},
"baseUrl": {
"title": "URL Base",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "完全にデバイス上で動作。APIキーやネットワークアクセスは不要。",
"requirements": "macOS Tahoe26.0以降を実行するApple Silicon Macが必要です。システム設定でApple Intelligenceを有効にする必要があります。"
"requirements": "macOS Tahoe26.0以降を実行するApple Silicon Macが必要です。システム設定でApple Intelligenceを有効にする必要があります。",
"unavailable": "このデバイスではApple Intelligenceを利用できません。macOS Tahoe26.0以降を実行し、システム設定でApple Intelligenceが有効になっているApple Silicon Macが必要です。"
},
"baseUrl": {
"title": "ベースURL",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Działa całkowicie na urządzeniu. Nie wymaga klucza API ani dostępu do sieci.",
"requirements": "Wymaga komputera Mac z Apple Silicon i systemem macOS Tahoe (26.0) lub nowszym. Apple Intelligence musi być włączone w Ustawieniach systemowych."
"requirements": "Wymaga komputera Mac z Apple Silicon i systemem macOS Tahoe (26.0) lub nowszym. Apple Intelligence musi być włączone w Ustawieniach systemowych.",
"unavailable": "Apple Intelligence nie jest dostępne na tym urządzeniu. Wymaga komputera Mac z Apple Silicon i systemem macOS Tahoe (26.0) lub nowszym z włączonym Apple Intelligence w Ustawieniach systemowych."
},
"baseUrl": {
"title": "Adres bazowy",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Интеллект",
"description": "Полностью работает на устройстве. Никакой ключ API или доступ к сети не требуется.",
"requirements": "Требуется Apple Silicon Mac под управлением macOS Tahoe (26.0) или более поздней версии. Apple Intelligence должен быть включен в настройках системы."
"requirements": "Требуется Apple Silicon Mac под управлением macOS Tahoe (26.0) или более поздней версии. Apple Intelligence должен быть включен в настройках системы.",
"unavailable": "Apple Intelligence недоступен на этом устройстве. Требуется Apple Silicon Mac под управлением macOS Tahoe (26.0) или более поздней версии с включенным Apple Intelligence в настройках системы."
},
"baseUrl": {
"title": "Базовый URL",

View file

@ -216,7 +216,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "Chạy hoàn toàn trên thiết bị. Không cần khóa API hoặc truy cập mạng.",
"requirements": "Yêu cầu Mac Apple Silicon chạy macOS Tahoe (26.0) trở lên. Apple Intelligence phải được bật trong Cài đặt Hệ thống."
"requirements": "Yêu cầu Mac Apple Silicon chạy macOS Tahoe (26.0) trở lên. Apple Intelligence phải được bật trong Cài đặt Hệ thống.",
"unavailable": "Apple Intelligence không khả dụng trên thiết bị này. Yêu cầu Mac Apple Silicon chạy macOS Tahoe (26.0) trở lên với Apple Intelligence được bật trong Cài đặt Hệ thống."
},
"baseUrl": {
"title": "URL cơ sở",

View file

@ -215,7 +215,8 @@
"appleIntelligence": {
"title": "Apple Intelligence",
"description": "完全在设备上运行。无需 API 密钥或网络访问。",
"requirements": "需要运行 macOS Tahoe26.0)或更高版本的 Apple Silicon Mac。必须在系统设置中启用 Apple Intelligence。"
"requirements": "需要运行 macOS Tahoe26.0)或更高版本的 Apple Silicon Mac。必须在系统设置中启用 Apple Intelligence。",
"unavailable": "Apple Intelligence 在此设备上不可用。需要运行 macOS Tahoe26.0)或更高版本的 Apple Silicon Mac并在系统设置中启用 Apple Intelligence。"
},
"baseUrl": {
"title": "基础 URL",