{"record":{"id":"0f2371fa8b693fe5","repo":"AprilNEA/OpenLogi","slug":"e-queries","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/openlogi-desktop/src/services/assets/queries.rs","lineNumber":106,"sourceCode":"    client: &SwrClient,\n    preference: AssetSourcePreference,\n    target: AssetTarget,\n    tx: UnboundedSender<bool>,\n    cx: &AsyncApp,\n) -> Task<()> {\n    let weak = client.downgrade();\n    let handle = client.subscribe(\n        (ROOT, \"model\", model_key(&target)),\n        move |_| {\n            let weak = weak.clone();\n            let target = target.clone();\n            async move {\n                let Some(client) = weak.upgrade() else {\n                    anyhow::bail!(\"client dropped before the registry resolved\");\n                };\n                let registry = registry(&client, preference)\n                    .await\n                    .map_err(|e| anyhow::anyhow!(\"{e}\"))?;\n                sync_target(&registry, &target)\n            }\n        },\n        default_options(),\n    );\n    settled_outcomes(handle, tx, \"asset sync\", cx)\n}\n\n/// Watch the registry on its own, for the window before any device has appeared.\n///\n/// The old scheduler special-cased this (\"fetch the index even with no\n/// devices\") so resolution works the moment a first device shows up. Once\n/// devices exist their own entries depend on this one, and this subscription\n/// just keeps it warm.\npub(crate) fn watch_index(\n    client: &SwrClient,\n    preference: AssetSourcePreference,\n    tx: UnboundedSender<bool>,","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/AprilNEA/OpenLogi/blob/e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8/crates/openlogi-desktop/src/services/assets/queries.rs#L88-L124","documentation":"In the desktop asset service's `watch_model`, a deferred async closure resolves the asset registry through a weak client handle. If the client was dropped before the registry resolved (`weak.upgrade()` returns `None`), the closure bails with this message. It is a lifecycle race: the model-watch outlived the client that started it.","triggerScenarios":"A model-watch task is spawned with a `Weak` client; by the time the async closure runs, the strong client has been released (window/service torn down), so `weak.upgrade()` yields `None` and `anyhow::bail!(\"client dropped before the registry resolved\")` fires.","commonSituations":"Rapid window close/open cycles during startup or shutdown; a settings change triggering a re-watch while the old watch is still in flight; agent disconnect causing the service to release its client while pending watches are queued.","solutions":["Treat it as benign cancellation during teardown — drop/skip the outcome instead of surfacing it as a user error.","If it appears in normal operation, check that the service keeps a strong client reference for the lifetime of the watch.","Ensure watches are cancelled (the handle dropped) before the client is released.","Re-open/re-poll the assets view; the next watch with a live client will sync normally."],"exampleFix":"// before\nlet Some(client) = weak.upgrade() else {\n    anyhow::bail!(\"client dropped before the registry resolved\");\n};\n\n// after — treat teardown as a no-op, not an error\nlet Some(client) = weak.upgrade() else {\n    return Ok(()); // client gone: watch cancelled during teardown\n};","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// treat a dropped client as normal cancellation, not an error\nlet Some(client) = weak.upgrade() else {\n    tracing::debug!(\"asset watch cancelled: client dropped\");\n    return Ok(());\n};","tryCatchPattern":null,"preventionTips":["Hold a strong client reference for the lifetime of any spawned watch task.","Cancel watches (drop their handles) before releasing the client during teardown.","Filter this outcome out of user-visible error reporting; log it at debug level."],"tags":["desktop","lifecycle","race-condition","async"],"backgroundTag":"invalid-state-transition","analyzedSha":"e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8","analyzedAt":"2026-09-13T03:07:16.451Z","contentChangedAt":"2026-09-13T03:07:16.451Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}