astral-sh/ruff · error

`initLogging` to only be called at most once.

Error message

`initLogging` to only be called at most once.

What it means

`initLogging` is the wasm-bindgen entry point that installs the console logger for the ty WASM build via `console_log::init_with_level`. The underlying logger can only be installed once per WASM instance, so calling `initLogging` a second time makes `init_with_level` return `Err`, and the `.expect` turns that into a panic with this message.

Source

Thrown at crates/ty_wasm/src/lib.rs:84

    // When the `console_error_panic_hook` feature is enabled, we can call the
    // `set_panic_hook` function at least once during initialization, and then
    // we will get better error messages if our code ever panics.
    //
    // For more details see
    // https://github.com/rustwasm/console_error_panic_hook#readme
    #[cfg(feature = "console_error_panic_hook")]
    console_error_panic_hook::set_once();
}

/// Initializes the logger with the given log level.
///
/// ## Panics
/// If this function is called more than once.
#[wasm_bindgen(js_name = "initLogging")]
pub fn init_logging(level: LogLevel) {
    console_log::init_with_level(level.into())
        .expect("`initLogging` to only be called at most once.");
}

#[derive(Copy, Clone, Debug)]
#[wasm_bindgen]
pub enum LogLevel {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
}

impl From<LogLevel> for log::Level {
    fn from(level: LogLevel) -> Self {
        match level {
            LogLevel::Trace => log::Level::Trace,
            LogLevel::Debug => log::Level::Debug,
            LogLevel::Info => log::Level::Info,

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Call `initLogging` exactly once, at the earliest application bootstrap point, before any other ty_wasm call.
  2. Guard the call site in JS with a module-level `let loggingInitialized` flag so repeat calls are skipped.
  3. Wrap the call in try/catch for idempotent best-effort initialization, tolerating the already-initialized error.
  4. Recreate/reload the WASM module if a different log level is genuinely needed.

Example fix

// before
import { initLogging, LogLevel } from './ty_wasm';
initLogging(LogLevel.Warn);
// on hot reload / rerun:
initLogging(LogLevel.Debug); // panics

// after
let initialized = false;
export function setup(level) {
  if (!initialized) {
    initLogging(level);
    initialized = true;
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// JS guard before calling
if (!globalThis.__tyLoggingInitialized) {
  globalThis.__tyLoggingInitialized = true;
  initLogging(LogLevel.Warn);
}

Type guard

function canInitLogging() {
  return typeof globalThis.__tyLoggingInitialized === 'undefined';
}

Try / catch

try {
  initLogging(LogLevel.Warn);
} catch (e) {
  // logger already installed; tolerate repeated init attempts
  console.debug('initLogging skipped:', e);
}

Prevention

When it happens

Trigger: Calling `initLogging(level)` from JavaScript more than once on the same module instance — e.g. calling it on every effect or page-load handler, on hot-reload, or from both an app bootstrap and a test harness — without reinstantiating the WASM module.

Common situations: React StrictMode double-invoking effects in development, HMR re-running setup code, integration tests initializing logging per test while reusing one module, or a library consumer and the app both calling `initLogging`.

Related errors


AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/890d83103078bff5. Report an issue: GitHub.