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
- 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.
- Await pending invoke() promises and call unlisten() for every listen()/Channel before triggering reload or navigation.
- Reduce full page reloads in dev: rely on HMR and avoid location.reload() in application logic.
- 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
- Re-register listeners and Channels in the framework mount effect so each reload generation has fresh callbacks; the warning then refers only to the dead generation.
- Await pending invoke() promises and unlisten() every listener before reload/navigation.
- Prefer HMR over full page reloads in development; avoid location.reload() in production logic.
- Scope Channels to a session and re-create them after reload instead of expecting callback ids to survive.
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
- IPC custom protocol failed, Tauri will now use the postMessa
- Unable to find your web assets, did you forget to build your
- Failed to install NPM {dependencies_str}
AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20).
Data as JSON: /api/errors/0fc4a3c2736d3a86.
Report an issue: GitHub.