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

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

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

Monitor the event bus. The IBuildInEvent enum in 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 to verify how PicList-Core normalizes names.

Validate package.json. Confirm the plugin's 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:

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:

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

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

Manually Resolving a Plugin Entry Point

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

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

Re-installing a Broken Plugin via CLI

When corruption occurs, completely reset the plugin state:


# 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 calls PluginLoader.registerPlugin after successful npm installation.

Source: PluginHandler.install registration flow

Key Source Files for Debugging

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

  • 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 — Provides CLI install/uninstall commands; invokes PluginLoader.registerPlugin after npm operations (lines 52-56).
  • src/utils/enum.ts — Defines IBuildInEvent constants used for error notifications and lifecycle hooks.
  • src/utils/common.ts — Contains getNormalPluginName and getProcessPluginName helpers for debugging name normalization issues.
  • 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.
  • 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 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 has a valid main field, and ensure the package actually exists in <baseDir>/node_modules. The fallback resolution at 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 lines 74-86 to see how the loader wraps the registration call in try-catch blocks.

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 →