m1k1o/neko · error

plugin's '%s' dependency: %w

Error message

plugin's '%s' dependency: %w

What it means

The plugin manager starts plugins as a dependency graph: startPlugin first recursively starts everything in a.dependsOn before starting the plugin itself. If a dependency's own startPlugin fails, the error is wrapped as fmt.Errorf("plugin's '%s' dependency: %w", a.plugin.Name(), err), attributing the failure to the dependent plugin's dependency chain while preserving the underlying cause via %w.

Source

Thrown at server/internal/plugins/dependency.go:46

		plug, ok := dep.findPlugin(name)
		if ok {
			return plug, true
		}
	}

	return nil, false
}

func (a *dependency) startPlugin(pm types.PluginManagers) error {
	if a.invoked {
		return nil
	}

	a.invoked = true

	for _, do := range a.dependsOn {
		if err := do.startPlugin(pm); err != nil {
			return fmt.Errorf("plugin's '%s' dependency: %w", a.plugin.Name(), err)
		}
	}

	err := a.plugin.Start(pm)
	if err != nil {
		return fmt.Errorf("plugin '%s' failed to start: %w", a.plugin.Name(), err)
	}

	a.logger.Info().Str("plugin", a.plugin.Name()).Msg("plugin started")
	return nil
}

type dependiencies struct {
	deps   map[string]*dependency
	logger zerolog.Logger
}

func (d *dependiencies) addPlugin(plugin types.Plugin) error {

View on GitHub (pinned to b0f01cedea)

Solutions

  1. Read the wrapped chain to the innermost error — it names the actual plugin whose Start() failed
  2. Enable/start the required dependency plugin first, or add it to the configuration
  3. Fix the root cause in the dependency plugin (config, external service, resource availability)
  4. If the dependency is optional, remove it from the plugin's dependsOn list

Example fix

// before (config)
plugins: {"whiteboard": {"enabled": true}, "chat": {"enabled": false}}
// after (chat is a whiteboard dependency)
plugins: {"whiteboard": {"enabled": true}, "chat": {"enabled": true}}
Defensive patterns

Strategy: try-catch

Validate before calling

for _, dep := range plugin.DependsOn() {
	if !pm.IsRegistered(dep) {
		return fmt.Errorf("plugin %s requires dependency %s which is not enabled", plugin.Name(), dep)
	}
}

Type guard

func dependenciesEnabled(deps []string, enabled map[string]bool) bool {
	for _, d := range deps {
		if !enabled[d] {
			return false
		}
	}
	return true
}

Try / catch

if err := manager.Start("whiteboard"); err != nil {
	var perr *pluginError
	if errors.As(err, &perr) && strings.Contains(err.Error(), "dependency") {
		log.Printf("dependency chain failed, disabling plugin: %v", err)
	} else {
		return err
	}
}

Prevention

When it happens

Trigger: Calling Start on a plugin whose dependsOn list contains a plugin that fails to start — either its own Start() returned an error or a deeper transitive dependency failed, so each level of the chain wraps the error with its own "plugin's 'X' dependency" prefix.

Common situations: A plugin enabled in config requires another plugin that is disabled or missing; the dependency plugin panics or returns an error during Start (bad config, failed port bind, missing external service); circular or stale dependency lists after a plugin rename or upgrade.

Related errors


AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01). Data as JSON: /api/errors/195f1932d177f7c8. Report an issue: GitHub.