can1357/oh-my-pi · error · Error

native spelling thread stopped

Error message

native spelling thread stopped

What it means

All native spelling work is dispatched to one dedicated, lazily spawned thread (`pi-native-spelling`) through a `flume` channel. This error is raised when the job cannot be queued (the sender's receiver half was dropped, i.e. the thread died) or the reply never arrives (the receiver was dropped), both signs the worker thread is gone. The process's spelling subsystem is unusable after this.

Source

Thrown at crates/pi-natives/src/spelling.rs:75

	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()
			.await
			.map_err(|_| Error::new(Status::GenericFailure, "native spelling thread stopped"))?
	}

	fn ns_range(start: u32, length: u32) -> Result<NSRange> {
		Ok(NSRange {
			location: usize::try_from(start)
				.map_err(|_| Error::new(Status::InvalidArg, "spelling range start is too large"))?,
			length:   usize::try_from(length)
				.map_err(|_| Error::new(Status::InvalidArg, "spelling range length is too large"))?,
		})
	}

	pub fn check(text: &str) -> Result<Vec<SpellingRange>> {
		let checker = checker()?;
		let text = NSString::from_str(text);

View on GitHub (pinned to 9690622007)

Solutions

  1. Treat the error as terminal for the spelling feature: catch it and permanently fall back to a JS spell checker for the session (retrying cannot revive the thread)
  2. Find and fix the panic that killed the worker thread — look for panics logged from the `pi-native-spelling` thread before this error appeared
  3. Avoid calling spelling APIs during process shutdown when the runtime is tearing down threads
  4. Restart the host process if the spelling feature is essential; the thread is respawned lazily on next launch

Example fix

// before
const guesses = await macOSSpellingGuesses(word, 0, word.length);
// after
let guesses = [];
try {
  guesses = await macOSSpellingGuesses(word, 0, word.length);
} catch (err) {
  if (String(err?.message).includes('native spelling thread stopped')) {
    spellingBroken = true; // stop calling native spelling this session
  }
  guesses = jsFallbackGuesses(word);
}
Defensive patterns

Strategy: try-catch

Validate before calling

let nativeSpellingAlive = true; // flip to false permanently on this error
async function guardedSpellingCall(fn, fallback) {
  if (!nativeSpellingAlive) return fallback();
  try { return await fn(); }
  catch (err) {
    if (String(err?.message).includes('native spelling thread stopped')) {
      nativeSpellingAlive = false;
      return fallback();
    }
    throw err;
  }
}

Type guard

function isSpellingThreadStopped(err) {
  return err instanceof Error && err.message === 'native spelling thread stopped';
}

Try / catch

try {
  result = await macOSAutocorrectWord(word, start, length);
} catch (err) {
  if (isSpellingThreadStopped(err)) {
    disableNativeSpelling(); // session-wide flag; never retry natively
    return jsAutocorrect(word);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling any async macOS spelling export after the dedicated spelling thread has terminated — e.g. the thread panicked while executing a previous job (the `while let Ok(job) = receiver.recv()` loop then ends on channel close, but a panic unwinds and kills the thread), or process teardown dropped the channel while an in-flight call awaited its reply.

Common situations: A previous `checkString:` call panicked inside AppKit on the worker thread, killing the loop; the host process is shutting down while a spelling promise is still pending; a latent panic in a job closure leaves every subsequent spelling call failing.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6ad2cdb5181267b2. Report an issue: GitHub.