# AUR0505

## Error Message

`AUR0505: Controller at <ControllerName> is in an unexpected state: <State> during deactivation.`

Where:

* `<ControllerName>` is the name of the controller.
* `<State>` is the unexpected state the controller was found in (e.g., `created`, `activating`, `deactivating`, `disposed`).

## Description

This error occurs during the deactivation phase of a component's lifecycle when Aurelia finds that the controller is not in one of the expected states (`bound` or `attached`). Deactivation should only proceed from these states; finding the controller in another state indicates a potential lifecycle mismatch or error in managing the component's state.

## Cause

This error often mirrors the causes of `AUR0503` but in the context of deactivation:

1. **Re-entrant Deactivation:** An attempt to deactivate a controller that is already in the process of deactivating or activating. This can happen with complex asynchronous operations or event handling within lifecycle methods.
2. **Lifecycle Interference:** Custom code directly manipulating the controller's state or calling lifecycle methods (like `deactivate`) out of the expected sequence.
3. **Concurrency Issues:** Multiple asynchronous operations trying to manipulate the same controller's lifecycle concurrently without proper coordination, leading to attempts to deactivate from an invalid state.
4. **Incomplete Activation:** If the activation process failed or was interrupted, the controller might be left in an intermediate state (e.g., `created`), and a subsequent deactivation attempt would then fail because it didn't reach `bound` or `attached`.
5. **Framework Bug (Less Common):** In rare cases, an internal issue within the Aurelia framework could lead to inconsistent state management.

## Solution

1. **Analyze Deactivation Triggers:** Determine what triggers the deactivation. Is it standard routing, removal via `if.bind`, `repeat.for`, or custom code manually calling `controller.deactivate()`?
2. **Simplify Lifecycle Logic:** Review the component's lifecycle hooks (`activate`, `binding`, `bound`, `attaching`, `detaching`, `unbinding`). Ensure they don't cause re-entrant deactivations or interfere with the expected state transitions. Pay close attention to asynchronous operations initiated in activation/attachment hooks and ensure they are properly handled or cancelled during deactivation/detachment hooks (`detaching`, `unbinding`).
3. **Ensure Proper Activation:** Verify that the component activates correctly and reaches the `attached` state before any deactivation attempt occurs. Fix any errors preventing successful activation.
4. **Synchronize Concurrent Operations:** If multiple asynchronous tasks can affect the component's lifecycle, ensure proper synchronization or state checking.
5. **Avoid Manual State/Lifecycle Calls:** Do not manually set `controller.state` or call `controller.deactivate` unless absolutely necessary and the implications are fully understood. Let Aurelia manage the lifecycle.
6. **Isolate the Problem:** Simplify the component and the scenario leading to the error to pinpoint the cause.

## Example

```typescript
import { customElement, ICustomElementController } from 'aurelia';

@customElement({ name: 'another-complex-lifecycle', template: '...' })
export class AnotherComplexLifecycle {
  public controller!: ICustomElementController<this>;
  private ongoingOperation: Promise<void> | null = null;
  private isAttached = false;

  attached() {
    this.isAttached = true;
    // Start some operation that might interact with state
    this.ongoingOperation = this.doSomethingAsync();
  }

  async detaching(initiator: ICustomElementController | null) {
    this.isAttached = false;
    console.log('Detaching...');

    // --- Potentially Problematic ---
    // If doSomethingAsync() somehow triggered a deactivate *again* on this
    // same controller while it's already in the detaching/deactivating phase,
    // it could lead to AUR0505.
    // Example:
    // if (this.ongoingOperation) {
    //   await this.ongoingOperation;
    //   // Imagine this tries to clean up by calling deactivate again:
    //   console.log('Operation finished, trying to ensure deactivation...');
    //   // Calling deactivate manually here is risky and likely wrong
    //   await this.controller.deactivate(initiator ?? this.controller, this.controller.parentController); // DANGEROUS
    // }
    // -----------------------------

    // --- Safer ---
    // Wait for ongoing operations if necessary, but let Aurelia handle the state transitions.
    // If cancellation is needed, handle it here without calling deactivate again.
    try {
        await this.ongoingOperation;
        console.log('Ongoing operation completed during detach.');
    } catch (e) {
        console.error('Error during ongoing operation cleanup:', e);
    }
  }

  async doSomethingAsync() {
    await new Promise(resolve => setTimeout(resolve, 200));
    if (!this.isAttached) {
        console.log('Operation finished after detach.');
        return;
    }
    console.log('Async operation completed while attached.');
    // Avoid triggering lifecycle changes from here without careful state checking.
  }
}

// --- External Code Example (Problematic) ---
// function triggerExternalDeactivation(controller: ICustomElementController) {
//   // If this is called when the controller is not 'bound' or 'attached'
//   // (e.g., already deactivating, or maybe never fully activated), it could cause AUR0505.
//   console.log(`Externally triggering deactivation for ${controller.name}`);
//   controller.deactivate(controller, controller.parentController)
//     .catch(err => console.error("External deactivation failed:", err)); // Might log AUR0505
// }
```

## Debugging Tips

* Identify the `<ControllerName>` and the unexpected `<State>` from the error message.
* Set breakpoints at the beginning of the `Controller#deactivate` method in `templating/controller.ts` and in the component's own deactivation hooks (`detaching`, `unbinding`). Inspect `controller.state`.
* Trace the call stack when the error occurs to understand what initiated the deactivation.
* Examine the component's activation sequence. Did it complete successfully and reach the `attached` state? Log the state in each lifecycle hook.
* Look for asynchronous operations started during activation/attachment. Are they properly handled or cancelled during deactivation/detachment? Could a delayed callback be attempting actions on a controller that's already deactivating?
* Search for manual calls to `controller.deactivate` or manipulation of `controller.state`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aurelia.io/developer-guides/error-messages/runtime-html/aur0505.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
