# How to Debug Plugin Loading Failures in PicList-Core: 9 Proven Strategies

> Debug PicList-Core plugin loading failures with 9 proven strategies. Enable verbose logging and capture errors to resolve issues quickly. Learn how to fix your plugin now.

- Repository: [Kuingsmile/piclist-core](https://github.com/kuingsmile/piclist-core)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Enable verbose logging with the `--debug` flag and listen for `notification` events to capture the exact error stack when PicList-Core fails to load a plugin.**

When extending the `kuingsmile/piclist-core` image upload framework, debugging plugin loading failures in PicList-Core requires tracing the internal `PluginLoader` lifecycle. The library discovers, resolves, imports, and registers plugins through a strict five-stage pipeline defined in [`src/lib/PluginLoader.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/lib/PluginLoader.ts). Understanding these stages allows you to determine whether a failure stems from naming mismatches, resolution errors, or runtime exceptions during registration.

## Understanding the Plugin Loading Lifecycle

The `PluginLoader` class orchestrates plugin initialization through distinct phases. Each stage has specific failure modes and corresponding source code locations.

### Stage 1: Discovery

In `PluginLoader.load()` at lines 47-63, the system scans [`package.json`](https://github.com/kuingsmile/piclist-core/blob/main/package.json) dependencies for names matching `^picgo-plugin-` or `^@…/picgo-plugin-`. It verifies that the resolved path exists in `node_modules`.

**Typical failures:**
- Missing or misspelled plugin names
- Plugin not installed in `node_modules`
- Corrupted [`package.json`](https://github.com/kuingsmile/piclist-core/blob/main/package.json) files

### Stage 2: Resolution

`PluginLoader.resolvePlugin()` (lines 38-45) uses the **`resolve`** library to locate the entry file. If resolution fails, it falls back to `<baseDir>/node_modules/<name>`.

**Typical failures:**
- Missing or incorrectly declared entry points (`main`, `module` fields)
- Malformed [`package.json`](https://github.com/kuingsmile/piclist-core/blob/main/package.json) in the plugin directory

### Stage 3: Loading

At lines 66-72, `PluginLoader.getPlugin()` dynamically imports the resolved file using `import()` and invokes the exported factory function.

**Typical failures:**
- Syntax errors in plugin source code
- Runtime errors during factory execution
- Missing peer dependencies such as `sharp`

### Stage 4: Registration

The plugin's `register()` method executes at lines 74-86, and the loader saves the plugin name to the config under `picgoPlugins.<name>`.

**Typical failures:**
- `register()` throws due to invalid configuration or missing API keys
- Conflicts with already-registered plugins

### Stage 5: Error Reporting

Lines 99-108 catch errors, remove the plugin from internal maps, and emit a **`IBuildInEvent.NOTIFICATION`** event. This surfaces the failure to the user interface or CLI output.

## 9 Strategies for Debugging Plugin Failures

### Enable Verbose Logging and Event Monitoring

**Run with debug flags.** Execute PicList-Core commands using `picgo -d` to activate the internal logger (`ctx.log`). This outputs the full error stack from the `catch` block in `PluginLoader.registerPlugin`.

**Listen for notification events.** When using PicList-Core programmatically, subscribe to the notification event to capture load failures:

```ts
picgo.on('notification', ({ title, body }) => {
  console.error(`[${title}]`, body)
})

```

**Monitor the event bus.** The `IBuildInEvent` enum in [`src/utils/enum.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/enum.ts) defines lifecycle events including `INSTALL`, `UNINSTALL`, `UPDATE`, and `NOTIFICATION`. Subscribing to these provides hooks into the full plugin lifecycle.

### Verify Plugin Configuration and Naming

**Check naming conventions.** Ensure the plugin name matches `picgo-plugin-xxx` or `@scope/picgo-plugin-xxx`. Use the `getNormalPluginName` helper in [`src/utils/common.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/common.ts) to verify how PicList-Core normalizes names.

**Validate package.json.** Confirm the plugin's [`package.json`](https://github.com/kuingsmile/piclist-core/blob/main/package.json) includes a valid `main` or `module` field pointing to the entry file. Verify the plugin folder exists under `<baseDir>/node_modules`.

**Clean conflicting registrations.** The loader checks `picgoPlugins.<name>` in config. A stale entry from a previous faulty registration can block new loads. Remove it using `picgo config-remove <uploader> <name>` and reinstall.

### Test Resolution and Isolation

**Manually resolve the entry file.** Replicate the loader's resolution logic to verify paths:

```ts
import resolve from 'resolve'
const entry = resolve.sync('picgo-plugin-xxx', { basedir: process.cwd() })
console.log('Resolved entry:', entry)

```

This mirrors the logic in `PluginLoader.resolvePlugin()`.

**Run the plugin in isolation.** Test the plugin outside the loader using Node REPL:

```js
const plugin = await import('picgo-plugin-xxx')
plugin.default(require('piclist').PicGo)

```

If this fails, the bug resides in the plugin code, not the loader.

**Inspect peer dependencies.** Upload plugins often require native binaries like `sharp`. Missing system libraries cause runtime errors during the Loading stage. Check the plugin README for required dependencies and ensure binaries are properly installed.

## Practical Code Examples

### Listening for Loading Errors Programmatically

Capture the exact exception that prevented registration by hooking into the notification system before calling `load()`:

```ts
import { PicGo } from 'piclist'

const picgo = new PicGo()

// Capture any notification including load errors
picgo.on('notification', ({ title, body }) => {
  console.error(`🔔 ${title}:`, body)
})

// Try to load all plugins (triggers the logger)
await picgo.pluginLoader.load()

```

[Source: `PluginLoader.registerPlugin` error handling](https://github.com/kuingsmile/piclist-core/blob/dev/src/lib/PluginLoader.ts#L99-L108)

### Manually Resolving a Plugin Entry Point

Use the `resolve` library to verify which file PicList-Core attempts to import:

```ts
import resolve from 'resolve'
import path from 'node:path'

function resolvePluginEntry(name: string, baseDir: string): string {
  try {
    // First attempt using resolve library
    return resolve.sync(name, { basedir: baseDir })
  } catch {
    // Fallback to conventional location
    return path.join(baseDir, 'node_modules', name)
  }
}

const entry = resolvePluginEntry('picgo-plugin-smms', process.cwd())
console.log('Plugin entry:', entry)

```

[Source: `PluginLoader.resolvePlugin`](https://github.com/kuingsmile/piclist-core/blob/dev/src/lib/PluginLoader.ts#L38-L45)

### Re-installing a Broken Plugin via CLI

When corruption occurs, completely reset the plugin state:

```bash

# Remove corrupted files

npm uninstall picgo-plugin-smms

# Clear PicList-Core config entry

picgo config-remove smms Default

# Re-install and register

npm install picgo-plugin-smms --save
picgo install picgo-plugin-smms

```

The `PluginHandler.install` method in [`src/lib/PluginHandler.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/lib/PluginHandler.ts) calls `PluginLoader.registerPlugin` after successful npm installation.

[Source: `PluginHandler.install` registration flow](https://github.com/kuingsmile/piclist-core/blob/dev/src/lib/PluginHandler.ts#L52-L56)

## Key Source Files for Debugging

Understanding these files allows you to insert debug breakpoints and trace execution:

- **[`src/lib/PluginLoader.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/lib/PluginLoader.ts)** — Core class handling discovery (lines 47-63), resolution (38-45), loading (66-72), registration (74-86), and error reporting (99-108).
- **[`src/lib/PluginHandler.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/lib/PluginHandler.ts)** — Provides CLI install/uninstall commands; invokes `PluginLoader.registerPlugin` after npm operations (lines 52-56).
- **[`src/utils/enum.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/enum.ts)** — Defines `IBuildInEvent` constants used for error notifications and lifecycle hooks.
- **[`src/utils/common.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/common.ts)** — Contains `getNormalPluginName` and `getProcessPluginName` helpers for debugging name normalization issues.
- **[`src/types/index.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/types/index.ts)** — TypeScript definitions for `IPicGoPlugin` and `IPluginLoader` interfaces, clarifying expected plugin structures.

## Summary

- **PicList-Core loads plugins through five stages**: Discovery, Resolution, Loading, Registration, and Error Reporting, each with distinct failure points in [`PluginLoader.ts`](https://github.com/kuingsmile/piclist-core/blob/main/PluginLoader.ts).
- **Enable `--debug` mode** and listen for `notification` events to capture the full error stack when `registerPlugin` fails.
- **Verify plugin names** against the `picgo-plugin-` convention using helpers in [`src/utils/common.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/common.ts) to catch naming mismatches.
- **Test resolution manually** using the `resolve` library to confirm `node_modules` paths and entry points are correctly configured.
- **Inspect peer dependencies** like `sharp` when encountering runtime errors during the Loading stage.
- **Clean stale configurations** using `picgo config-remove` to resolve conflicts from previous failed registrations.

## Frequently Asked Questions

### How do I identify which stage is failing when a plugin won't load?

Check the error message emitted through the `notification` event or visible with `--debug` mode. If the error mentions "cannot find module," the failure occurs during **Resolution** (lines 38-45). If the error shows a stack trace from the plugin's own code, the failure happens during **Loading** (lines 66-72) or **Registration** (lines 74-86).

### Why does PicList-Core say a plugin is installed but not loaded?

The loader requires the config flag `picgoPlugins.<name>` to be `true` or `undefined`. If a previous error left a stale entry, or if the config value is `false`, the plugin exists in `node_modules` but remains unregistered. Run `picgo config-remove <uploader> <name>` to clear the entry, then reinstall.

### What causes "Cannot find module" errors for plugins I just installed?

This typically occurs during the **Resolution** stage when the `resolve` library cannot find the entry file. Verify the plugin's [`package.json`](https://github.com/kuingsmile/piclist-core/blob/main/package.json) has a valid `main` field, and ensure the package actually exists in `<baseDir>/node_modules`. The fallback resolution at [`src/lib/PluginLoader.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/lib/PluginLoader.ts) lines 38-45 may also fail if the directory structure is non-standard.

### How can I debug a plugin that crashes during initialization?

Use the isolation strategy: import the plugin directly in a Node REPL and invoke its factory function with a `PicGo` instance. If it crashes outside the loader, the bug is in the plugin's `register()` method or missing peer dependencies like native binaries. Check [`src/lib/PluginLoader.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/lib/PluginLoader.ts) lines 74-86 to see how the loader wraps the registration call in try-catch blocks.