tauri-apps/tauri · warning

[TAURI] Couldn't find callback id ${id}. This might happen w

Error message

[TAURI] Couldn't find callback id ${id}. This might happen when the app is reloaded while Rust is running an asynchronous operation.

What it means

core.js keeps a Map of JS callbacks registered via window.__TAURI_INTERNALS__.transformCallback (the machinery behind invoke, events and Channels). When Rust finishes an asynchronous operation it calls runCallback(id, data); if the id is no longer in the Map — classically because the page reloaded or navigated, wiping all registered callbacks — Tauri logs this console.warn instead of delivering. It is a warning, not an exception: the pending response is dropped and nothing crashes.

Source

Thrown at crates/tauri/scripts/core.js:44

    callbacks.set(identifier, (data) => {
      if (once) {
        unregisterCallback(identifier)
      }
      return callback && callback(data)
    })
    return identifier
  }

  function unregisterCallback(id) {
    callbacks.delete(id)
  }

  function runCallback(id, data) {
    const callback = callbacks.get(id)
    if (callback) {
      callback(data)
    } else {
      console.warn(
        `[TAURI] Couldn't find callback id ${id}. This might happen when the app is reloaded while Rust is running an asynchronous operation.`
      )
    }
  }

  // Maybe let's rename it to `registerCallback`?
  Object.defineProperty(window.__TAURI_INTERNALS__, 'transformCallback', {
    value: registerCallback
  })

  Object.defineProperty(window.__TAURI_INTERNALS__, 'unregisterCallback', {
    value: unregisterCallback
  })

  Object.defineProperty(window.__TAURI_INTERNALS__, 'runCallback', {
    value: runCallback
  })

View on GitHub (pinned to 52e4b6e71d)

Solutions

  1. Usually no action needed: if listeners are re-registered on page load (framework mount effect), the warning refers to the pre-reload generation and is harmless.
  2. Await pending invoke() promises and call unlisten() for every listen()/Channel before triggering reload or navigation.
  3. Reduce full page reloads in dev: rely on HMR and avoid location.reload() in application logic.
  4. For long-running streams, create Channels per session and re-create them after reload instead of expecting ids to survive.

Example fix

// before: reload while a Rust async op is in flight — its callback id
// is gone when the response arrives, triggering the warning
invoke('export_file').catch(() => {})
location.reload()

// after: let the operation finish (or fail) before reloading
await invoke('export_file')
location.reload()
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: An in-flight invoke()/Channel/event delivery that resolves after location.reload(), a dev-server full reload (vite HMR fallback, manual Ctrl+R), or navigation to another URL; also delivering an event after unlisten()/unregisterCallback already removed the id.

Common situations: Development with hot reload while long-running Rust commands (export, download, shell) are pending; apps that navigate between pages and lose the callback registry; SPA listeners not torn down before route changes; firing events at a webview that just reloaded.

Related errors


AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20). Data as JSON: /api/errors/0fc4a3c2736d3a86. Report an issue: GitHub.