can1357/oh-my-pi · error · Error
failed to initialize AppKit
Error message
failed to initialize AppKit
What it means
On macOS, the spelling module initializes AppKit exactly once per process via `NSApplicationLoad()` and caches the result in a `LazyLock`. If that call returns false, every spelling API (`check`, `completions`, `guesses`, `correction`) fails with this `GenericFailure` because `NSSpellChecker` requires an initialized AppKit. On non-macOS builds these APIs are no-ops and never throw this.
Source
Thrown at crates/pi-natives/src/spelling.rs:59
})
.expect("failed to spawn the native spelling thread");
sender
});
static APP_KIT_LOADED: LazyLock<bool> = LazyLock::new(|| {
// SAFETY: AppKit documents `NSApplicationLoad` as process-global and
// idempotent; `LazyLock` guarantees this process calls it at most once.
unsafe { NSApplicationLoad() }
});
const NS_NOT_FOUND: usize = isize::MAX as usize;
#[link(name = "AppKit", kind = "framework")]
unsafe extern "C" {
fn NSApplicationLoad() -> bool;
}
fn checker() -> Result<Retained<NSSpellChecker>> {
if !*APP_KIT_LOADED {
return Err(Error::new(Status::GenericFailure, "failed to initialize AppKit"));
}
let checker = NSSpellChecker::sharedSpellChecker();
checker.setAutomaticallyIdentifiesLanguages(true);
Ok(checker)
}
pub async fn run<T>(work: impl FnOnce() -> Result<T> + Send + 'static) -> Result<T>
where
T: Send + 'static,
{
let (reply, result) = flume::bounded(1);
SPELLING_THREAD
.send(Box::new(move || {
let _ = reply.send(work());
}))
.map_err(|_| Error::new(Status::GenericFailure, "native spelling thread stopped"))?;
result
.recv_async()View on GitHub (pinned to 9690622007)
Solutions
- Check the exported `macOSSpellCheckerAvailable()` and treat false/throwing as 'no spelling support', degrading to a JS-side fallback (e.g. a wordlist)
- Ensure the process runs inside a logged-in Aqua GUI session on macOS, not over SSH or in a headless CI job
- Call the spelling APIs only after basic AppKit initialization in the host app (main NSApplication setup) if embedding
- Wrap calls in try/catch and fall back to a non-native spell checker when this error surfaces
Example fix
// before
const ranges = await macOSCheckSpelling(text);
// after
let ranges = [];
if (macOSSpellCheckerAvailable()) {
try { ranges = await macOSCheckSpelling(text); }
catch { ranges = []; } // headless AppKit: fall back
}
ranges = ranges.length ? ranges : jsFallbackCheck(text); Defensive patterns
Strategy: fallback
Validate before calling
if (!macOSSpellCheckerAvailable()) {
// non-macOS or unavailable: skip native spelling entirely
return [];
}
// additionally, detect headless AppKit lazily via a probe call wrapped in try/catch Type guard
function nativeSpellingSupported() {
return typeof macOSSpellCheckerAvailable === 'function' && macOSSpellCheckerAvailable() === true;
} Try / catch
let ranges;
try {
ranges = await macOSCheckSpelling(text);
} catch (err) {
if (err?.code === 'GenericFailure' && String(err?.message).includes('failed to initialize AppKit')) {
return jsFallbackCheckSpelling(text); // headless environment
}
throw err;
} Prevention
- Gate native spelling calls behind macOSSpellCheckerAvailable()
- Expect failure in SSH/headless/CI macOS sessions and keep a JS spell-check fallback wired
- Call spelling APIs only from GUI-context processes with a WindowServer session
- Cache the degraded state after the first AppKit error instead of retrying every call
When it happens
Trigger: `macOSCheckSpelling`, `macOSCompleteWord`, `macOSAutocorrectWord`, or `macOSSpellingGuesses` called in a process where `NSApplicationLoad()` failed on first use — most commonly a headless or non-app context (plain CLI daemon, SSH session without a WindowServer connection, CI runner, embedded/JS-only runtime) on macOS.
Common situations: Running a GUI-dependent native module inside a headless test runner on macOS CI; launching the app over SSH where no Aqua session exists; calling the API before any NSApplication setup in a bare `node` script; sandboxed environments where AppKit cannot initialize.
Related errors
- InvalidArg
- native spelling thread stopped
- spelling range start is too large
- spelling range length is too large
- truncated getattrlistbulk record length
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/359a7618c7125ae6.
Report an issue: GitHub.