Skill v1.0.0
currentLLM-judged scan100/100version: "1.0.0" name: gpui-troubleshooting description: Common errors and solutions for GPUI development. Use when debugging build errors, runtime panics, borrow checker issues, or unexpected behavior.
GPUI Troubleshooting
This skill covers common errors and solutions when developing with GPUI.
Borrow Checker Errors
Multiple Borrow Error
Error: Cannot borrow cx as mutable more than once
Solution: Use the inner cx provided to closures, not the outer one
// ❌ WRONGentity.update(cx, |view, inner_cx| {view.count += 1;cx.notify(); // Using outer cx - ERROR!});// ✅ CORRECTentity.update(cx, |view, inner_cx| {view.count += 1;inner_cx.notify(); // Using inner cx});
Moved Value Error
Error: Use of moved value in async block
Solution: Clone before moving into async block
// ❌ WRONGcx.spawn(async move |this, cx| {self.data.do_something(); // ERROR: self moved});// ✅ CORRECTlet data = self.data.clone();cx.spawn(async move |this, cx| {data.do_something();});
Entity Errors
Update While Updating Panic
Error: already mutably borrowed: BorrowError
Cause: Trying to update an entity while it's already being updated
Solution: Avoid nested entity updates
// ❌ WRONG - will panicentity.update(cx, |view, cx| {entity.update(cx, |view2, cx2| {// Nested update - PANIC!});});// ✅ CORRECT - restructure logicentity.update(cx, |view, cx| {view.prepare_update();});// Update happens after first update completesentity.update(cx, |view, cx| {view.apply_update();});
WeakEntity Upgrade Failure
Error: Panic when calling methods on None
Solution: Always check upgrade() result
// ❌ WRONGlet entity = weak.upgrade().unwrap(); // May panic!// ✅ CORRECTif let Some(entity) = weak.upgrade() {entity.update(cx, |view, cx| {// ...});} else {// Entity was dropped}// ✅ ALSO CORRECT (in async context)this.update(&mut *cx, |view, cx| {// ...})?; // Propagate error if entity gone
Async Errors
"Nothing Left to Run" in Tests
Error: run_until_parked() completes but test fails
Cause: Using smol::Timer instead of GPUI timers
Solution: Use GPUI executor timers
// ❌ WRONGsmol::Timer::after(Duration::from_secs(1)).await;// ✅ CORRECTcx.background_executor.timer(Duration::from_secs(1)).await;
Task Dropped Before Completion
Error: Async work doesn't complete
Cause: Task dropped without being awaited or detached
Solution: Either detach or store the task
// ❌ WRONG - task dropped immediatelycx.spawn(async move |this, cx| {// Work...Ok(())});// ✅ CORRECT - detachcx.spawn(async move |this, cx| {// Work...Ok(())}).detach();// ✅ ALSO CORRECT - storeself.current_task = Some(cx.spawn(async move |this, cx| {// Work...Ok(())}));
Async Context Borrow Error
Error: Cannot dereference cx in async context
Solution: Use &mut *cx to dereference
// ❌ WRONGcx.spawn(async move |this, cx| {this.update(cx, |view, cx| { // ERROR!// ...});});// ✅ CORRECTcx.spawn(async move |this, cx| {this.update(&mut *cx, |view, cx| {// ...})?;Ok(())});
Trait Errors
IntoElement Not Implemented
Error: the trait IntoElement is not implemented for ...
Solution: Ensure type implements IntoElement or use .child() correctly
// ❌ WRONGdiv().child(some_struct); // Error if SomeStruct doesn't impl IntoElement// ✅ CORRECT - use entitylet entity = cx.new(|_| SomeStruct::new());div().child(entity);// ✅ ALSO CORRECT - implement RenderOnce#[derive(IntoElement)]struct SomeStruct;impl RenderOnce for SomeStruct {fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {div().child("Content")}}
Render Trait Signature Mismatch
Error: Method signature doesn't match trait
Solution: Ensure correct signature with Window and Context<Self>
// ❌ WRONGimpl Render for MyView {fn render(&mut self, cx: &mut Context<Self>) -> impl IntoElement {// Missing window parameter}}// ✅ CORRECTimpl Render for MyView {fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {div().child("Content")}}
UI Not Updating
Forgetting cx.notify()
Symptom: State changes but UI doesn't update
Solution: Call cx.notify() after state changes
// ❌ WRONGfn increment(&mut self, cx: &mut Context<Self>) {self.count += 1;// Missing cx.notify() - UI won't update!}// ✅ CORRECTfn increment(&mut self, cx: &mut Context<Self>) {self.count += 1;cx.notify(); // Trigger re-render}
Subscription Dropped
Symptom: Events stop being received
Solution: Store Subscription in a field
// ❌ WRONGimpl Parent {fn new(cx: &mut Context<Self>) -> Self {let child = cx.new(|_| Child::new());cx.subscribe(&child, |this, _child, event, cx| {// Handle event}); // Subscription dropped here!Self { child }}}// ✅ CORRECTstruct Parent {child: Entity<Child>,_subscription: Subscription, // Store to keep alive}impl Parent {fn new(cx: &mut Context<Self>) -> Self {let child = cx.new(|_| Child::new());let subscription = cx.subscribe(&child, |this, _child, event, cx| {// Handle event});Self {child,_subscription: subscription,}}}
Build Errors
Missing Imports
Error: Cannot find type/trait in this scope
Solution: Import from gpui::* or specific module
// Add at top of fileuse gpui::*;// Or specific importsuse gpui::{div, rgb, px,App, Context, Entity, Window,Render, IntoElement, RenderOnce,};
Clippy Warnings
Use project's clippy script:
# ❌ WRONGcargo clippy# ✅ CORRECT (for Zed-based projects)./script/clippy
Runtime Panics
Index Out of Bounds
Error: Panic from vector indexing
Solution: Use safe access methods
// ❌ WRONGlet item = self.items[index]; // May panic!// ✅ CORRECTif let Some(item) = self.items.get(index) {// Use item}// ✅ ALSO CORRECTif index < self.items.len() {let item = &self.items[index];}
Unwrap on None/Err
Error: called unwrap() on a None value
Solution: Never use unwrap() - use ? or proper error handling
// ❌ WRONGlet value = option.unwrap(); // May panic!let result = fallible_op().unwrap(); // May panic!// ✅ CORRECT - propagate errorlet value = option.ok_or_else(|| anyhow::anyhow!("Missing value"))?;let result = fallible_op()?;// ✅ ALSO CORRECT - handle explicitlymatch option {Some(value) => {// Use value}None => {// Handle missing case}}
Common Mistakes Summary
| Issue | Cause | Solution | |
|---|---|---|---|
| Multiple borrow | Using outer cx in closure | Use inner cx | |
| Update panic | Nested entity updates | Avoid nesting, restructure | |
| UI not updating | Missing cx.notify() | Call after state changes | |
| Task not running | Task dropped | Use .detach() or store task | |
| Test timeout | Using smol::Timer | Use GPUI timer() | |
| Events not received | Subscription dropped | Store in field | |
| Weak entity panic | Not checking upgrade() | Use if let Some(...) or ? | |
| Unwrap panic | Calling .unwrap() | Use ? or proper error handling |
Debugging Tips
- Check the inner cx: In update closures, always use the inner
cx - Verify notify calls: Add
cx.notify()after state changes - Store subscriptions: Keep
Subscriptionvalues in struct fields - Use GPUI timers in tests: Replace
smol::Timerwithcx.background_executor.timer() - Avoid unwrap: Use
?for error propagation or explicit error handling - Check task lifecycle: Ensure tasks are detached or stored, not dropped
- Watch for nested updates: Avoid updating entities within update closures