cjpais/Handy · warning

Failed to sync language from settings:

Error message

Failed to sync language from settings:

What it means

syncLanguageFromSettings reads the persisted AppSettings via the getAppSettings Tauri command; if no app_language is saved it falls back to the OS locale from @tauri-apps/plugin-os, then calls i18n.changeLanguage on the resolved supported code. Any rejection anywhere in that chain (store read, OS plugin, changeLanguage) lands in this catch and is logged; the app silently keeps i18next's default 'en'. Note it runs at module load time (line 125), which makes backend-not-ready races the dominant cause.

Source

Thrown at src/i18n/index.ts:120

// Sync language from app settings
export const syncLanguageFromSettings = async () => {
  try {
    const result = await commands.getAppSettings();
    if (result.status === "ok" && result.data.app_language) {
      const supported = getSupportedLanguage(result.data.app_language);
      if (supported && supported !== i18n.language) {
        await i18n.changeLanguage(supported);
      }
    } else {
      // Fall back to system locale detection if no saved preference
      const systemLocale = await locale();
      const supported = getSupportedLanguage(systemLocale);
      if (supported && supported !== i18n.language) {
        await i18n.changeLanguage(supported);
      }
    }
  } catch (e) {
    console.warn("Failed to sync language from settings:", e);
  }
};

// Run language sync on init
syncLanguageFromSettings();

// Listen for language changes to update HTML dir and lang attributes
i18n.on("languageChanged", (lng) => {
  const dir = getLanguageDirection(lng);
  updateDocumentDirection(dir);
  updateDocumentLanguage(lng);
});

// Re-export RTL utilities for convenience
export { getLanguageDirection, isRTLLanguage } from "@/lib/utils/rtl";

export default i18n;

View on GitHub (pinned to c89b7bf389)

Solutions

  1. Check the logged error object first: 'command not found' points to registration, a plugin permission error points to capabilities
  2. Re-invoke syncLanguageFromSettings from App.tsx after onboarding/ready instead of relying solely on the module-load call
  3. Ensure the store and os plugins are registered in Rust and listed in tauri.conf.json capabilities
  4. Guard with isTauri() so browser-only development does not spam the warning

Example fix

// before (src/i18n/index.ts, module scope)
// Run language sync on init
syncLanguageFromSettings();

// after: keep module-scope call but add a ready-time retry in App.tsx
useEffect(() => {
  if (onboardingStep === "done") void syncLanguageFromSettings();
}, [onboardingStep]);
Defensive patterns

Strategy: retry

Validate before calling

import { isTauri } from "@tauri-apps/api/core";
if (!isTauri()) return; // avoid invoking outside the Tauri shell

Try / catch

try { await syncLanguageFromSettings(); }
catch { /* sync itself swallows; re-run after ready */ }

Prevention

When it happens

Trigger: Module evaluated before the Tauri backend/store plugin is initialized (it is called at import time, not after app ready); plugin-os not installed or missing from capabilities so locale() rejects; the settings store file unreadable/corrupted; frontend executed in a plain browser where every invoke rejects.

Common situations: Startup races on slower disks; capability (permissions) misconfiguration after upgrading Tauri; running the Vite dev server standalone; a first run where the store file does not exist yet and the command errors instead of returning defaults.

Related errors


AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17). Data as JSON: /api/errors/cbe66b392e55d2e1. Report an issue: GitHub.