Cordis Fiber Plugin Lifecycle States Explained: A Complete Technical Guide

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 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 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:

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.

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:

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:

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):

// 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.
  • 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →