# Cordis Fiber Plugin Lifecycle States Explained: A Complete Technical Guide

> Master Cordis Fiber plugin lifecycle states PENDING LOADING ACTIVE FAILED DISPOSED UNLOADING. Learn how state machines ensure predictable initialization error recovery and cleanup in this technical guide.

- Repository: [Cordiverse/cordis](https://github.com/cordiverse/cordis)
- Tags: deep-dive
- Published: 2026-08-25

---

**Cordis Fiber plugins transition through six deterministic states—PENDING, LOADING, ACTIVE, FAILED, DISPOSED, and UNLOADING—managed by the `FiberState` enum and state machine logic in [`packages/core/src/fiber.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/fiber.ts) to ensure predictable plugin initialization, error recovery, and resource cleanup.**

The Cordis framework powers modular bot development through a sophisticated **Fiber** abstraction that governs every plugin's lifecycle. Understanding these Cordis Fiber plugin lifecycle states is essential for writing reliable plugins, debugging initialization failures, and implementing graceful shutdown procedures in production environments.

## The Six Cordis Fiber Plugin Lifecycle States

The `FiberState` enum defined at line 78 in [`packages/core/src/fiber.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/fiber.ts) represents the complete lifecycle of a Cordis plugin. Each state corresponds to specific internal conditions and transition triggers within the fiber's state machine.

### PENDING State

**PENDING** indicates the fiber is instantiated but not yet loaded. This is the default state assigned in the `Fiber` constructor at line 107, where `state = FiberState.PENDING` initializes the fiber before any effects execute. The plugin exists in memory but has not begun executing its setup code or registering disposables.

### LOADING State

**LOADING** occurs when the plugin's code executes and its disposables register with the context. The `_setEpoch` method sets this state at line 407 when the epoch transitions from inactive to a concrete value. During this phase, Cordis executes the plugin's main effect inside `_reload`, copying current implementations into `this.store` and handling any asynchronous initialization work.

### ACTIVE State

**ACTIVE** signifies the plugin has successfully loaded and is running normally. This state is assigned after `_reload` completes without error at line 203, specifically when `FiberState.ACTIVE` is set. At this point, the plugin can respond to events, expose services through the context, and operate as a fully integrated system component.

### FAILED State

**FAILED** indicates an error occurred during loading or execution. The `_getState` method determines this state at line 350 when `this._error` is set—typically when a thrown exception occurs in the plugin's effect or during disposable cleanup. This state prevents the plugin from interfering with system stability while preserving error information for debugging.

### DISPOSED State

**DISPOSED** means the fiber has been fully torn down and cannot accept new effects. This occurs when `uid` becomes `null` after the `dispose` callback runs, checked at line 349 in `_getState`. Once disposed, the fiber releases all resources and removes itself from the parent's tracking system.

### UNLOADING State

**UNLOADING** represents the transitional phase where the plugin's disposables are being cleared, typically during restart or shutdown sequences. The `_setEpoch` method triggers this at line 410 when the epoch switches back to inactive. The `_unload` method then clears disposables and empties `this.store` before the fiber potentially reloads.

## How Cordis Fiber State Transitions Work

### State Determination via _getState

The `Fiber` class derives the current state dynamically through the private `_getState` method. This function inspects internal flags to determine which `FiberState` value represents the fiber's current condition:

```typescript
private _getState() {
  if (this.uid === null) return FiberState.DISPOSED;
  if (this._error) return FiberState.FAILED;
  if (this._runner.epoch !== INACTIVE) return FiberState.ACTIVE;
  return FiberState.PENDING;
}

```

This logic prioritizes terminal states—checking disposal first, then errors, then active status—ensuring accurate reporting even when multiple conditions might apply. The method is called whenever the framework broadcasts status changes through the `internal/status` event defined in [`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts).

### Epoch Management and _setEpoch

All major state transitions flow through the `_setEpoch` method at line 99. This method mediates between **LOADING** and **UNLOADING** states by comparing the new epoch value against `INACTIVE`:

- When receiving a concrete epoch string, `_setEpoch` triggers **LOADING** via `_reload`
- When receiving `INACTIVE`, it initiates **UNLOADING** via `_unload`

The epoch system allows Cordis to handle hot-reloading scenarios where a plugin must unload its current implementation before loading a new version, ensuring clean state transitions without resource leaks.

## Practical Examples for Managing Plugin Lifecycle

### Observing State Changes

Monitor fiber transitions by listening to the `internal/status` event. This approach is useful for logging, metrics, or triggering side effects when specific plugins reach certain states:

```typescript
import { Context } from 'cordis'

const ctx = new Context()

ctx.on('internal/status', (fiber, oldState) => {
  console.log(`Fiber ${fiber.name} transitioned from ${oldState} to ${fiber.state}`)
})

ctx.plugin(async (ctx) => {
  // This code executes during LOADING state
  ctx.console.log('Initializing plugin services...')
  // State becomes ACTIVE after this function completes
})

```

### Handling Failed Plugins

When a plugin throws during initialization or runtime, Cordis captures the error and transitions the fiber to **FAILED**. Use `await()` to catch these errors and implement recovery logic:

```typescript
ctx.plugin(() => {
  // Simulating a configuration error
  throw new Error('Invalid API key configuration')
})

// Wait for plugin resolution and handle failures
ctx.fiber.await().catch(err => {
  console.error('Plugin failed to load:', err.message)
  console.log('Current fiber state:', ctx.fiber.state) // FAILED
})

```

### Restarting Plugins

Force a plugin through the full unload/load cycle using the `restart()` method. This triggers **UNLOADING** (disposal of current resources) followed by **LOADING** (re-execution of the plugin effect):

```typescript
// Assume myPlugin was previously loaded and is ACTIVE
const pluginHandle = ctx.plugin(myPlugin)

// Force restart cycle: ACTIVE → UNLOADING → LOADING → ACTIVE
await pluginHandle.restart()

console.log('Post-restart state:', ctx.fiber.state) // ACTIVE

```

The restart process calls `_setEpoch(INACTIVE)` to initiate unloading, clears all disposables through `_unload`, then proceeds with `_reload` to reinitialize the plugin with fresh state.

## Summary

- **Cordis Fiber plugin lifecycle states** consist of six distinct phases: PENDING, LOADING, ACTIVE, FAILED, DISPOSED, and UNLOADING, defined in [`packages/core/src/fiber.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/fiber.ts).
- The `_getState` method dynamically determines the current state by checking disposal status, error flags, and epoch activity in that priority order.
- State transitions are managed through `_setEpoch`, which coordinates the loading and unloading cycles based on epoch values.
- The `internal/status` event in [`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts) broadcasts state changes to listeners, enabling reactive lifecycle management.
- Failed plugins enter the **FAILED** state and preserve error information, while disposed plugins with null `uid` values enter **DISPOSED** and become inert.

## Frequently Asked Questions

### How do I check the current state of a Cordis Fiber plugin?

Access the `state` property on any fiber instance. The value is computed on-demand by `_getState` and reflects the current lifecycle phase. For the root context, use `ctx.fiber.state` to inspect the overall application state, which typically progresses from PENDING through LOADING to ACTIVE during startup.

### What triggers the UNLOADING state in Cordis Fiber?

The **UNLOADING** state triggers when `_setEpoch` receives the `INACTIVE` value, occurring during `restart()` calls or when the parent context disposes the plugin. This transition initiates the `_unload` method, which synchronously clears all disposables and empties the implementation store before the fiber potentially reloads or shuts down completely.

### How does Cordis handle plugin execution errors?

When a plugin's effect function throws or a disposable fails, Cordis catches the exception in `_reload`, stores it in `this._error`, and transitions the fiber to **FAILED** state. This prevents partial initialization from polluting the context and allows calling code to detect failures through `await()` or `restart()` promise rejections.

### Can I listen to Cordis Fiber state changes in real-time?

Yes. Subscribe to the `internal/status` event on the context to receive notifications whenever any fiber transitions between states. The event handler receives the fiber instance and its previous state, enabling you to build monitoring, logging, or orchestration logic that responds to specific lifecycle transitions across your application.