matryer/xbar · error

read plugin source

Error message

read plugin source

What it means

This error is wrapped by loadVariablesFromPluginMetadata when io.ReadAll fails while reading the opened plugin file's contents (capped at 1MB via io.LimitReader). It means the plugin file was successfully opened but its bytes could not be read from disk (I/O failure after open). The library wraps the underlying OS error so callers know the failure occurred at the read-plugin-source step of variable extraction.

Source

Thrown at pkg/plugins/variables.go:130

		return nil, err
	}
	var vars map[string]interface{}
	if err := json.Unmarshal(b, &vars); err != nil {
		return nil, errors.Wrap(err, "json.Unmarshal")
	}
	return vars, nil
}

func (p *Plugin) loadVariablesFromPluginMetadata() (map[string]interface{}, error) {
	// read the plugin metadata for default values
	pluginFile, err := os.Open(p.Command)
	if err != nil {
		return nil, errors.Wrap(err, "open plugin source")
	}
	defer pluginFile.Close()
	pluginFileB, err := io.ReadAll(io.LimitReader(pluginFile, 1_000_000))
	if err != nil {
		return nil, errors.Wrap(err, "read plugin source")
	}
	pluginMetadata, err := metadata.Parse(metadata.DebugFunc(p.Debugf), p.CleanFilename(), string(pluginFileB))
	if err != nil {
		return nil, errors.Wrap(err, "metadata.Parse")
	}
	vars := make(map[string]interface{})
	for _, pluginVar := range pluginMetadata.Vars {
		if pluginVar.Default == "" {
			// skip values with no default
			continue
		}
		vars[pluginVar.Name] = pluginVar.DefaultValue()
	}
	return vars, nil
}

View on GitHub (pinned to d624239058)

Solutions

  1. Check the wrapped cause (%+v of the error) for the underlying errno (EIO, ESTALE, EACCES) and fix the filesystem/permission issue
  2. Re-download or reinstall the plugin file — it is likely corrupted, truncated, or missing
  3. Verify the plugin path points to a regular readable file (os.Stat + Mode().IsRegular()) before loading
  4. Move plugin files off network/unstable mounts to local disk
  5. Rerun the load; transient I/O errors may not recur

Example fix

// before
pluginFileB, err := io.ReadAll(io.LimitReader(pluginFile, 1_000_000))
if err != nil {
	return nil, errors.Wrap(err, "read plugin source")
}
// after
if fi, statErr := os.Stat(path); statErr != nil || !fi.Mode().IsRegular() {
	return nil, fmt.Errorf("plugin %s is not a regular file", path)
}
pluginFileB, err := io.ReadAll(io.LimitReader(pluginFile, 1_000_000))
if err != nil {
	return nil, errors.Wrap(err, "read plugin source")
}
Defensive patterns

Strategy: validation

Validate before calling

func pluginReadable(path string) error {
	fi, err := os.Stat(path)
	if err != nil {
		return err
	}
	if !fi.Mode().IsRegular() {
		return fmt.Errorf("%s is not a regular file", path)
	}
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	f.Close()
	return nil
}

Try / catch

vars, err := loadVariablesFromPluginMetadata(p)
if err != nil {
	if strings.Contains(fmt.Sprintf("%+v", err), "read plugin source") {
		log.Printf("plugin file unreadable, skipping: %v", err)
		return defaultVars, nil
	}
	return nil, err
}

Prevention

When it happens

Trigger: os.Open succeeded on pkg/plugins file (pkg/plugins/variables.go:130) but io.ReadAll(io.LimitReader(pluginFile, 1_000_000)) returned a non-nil error — e.g. file deleted/truncated between open and read, disk I/O error, read permission revoked mid-read, or a device/FIFO that errors on read.

Common situations: Plugin binary replaced or removed while the app loads variables from it; network-mounted plugin directory with flaky connectivity; filesystem errors (bad sectors, EIO); plugin file is a special device that fails on read; SELinux/AppArmor denying read after open.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02). Data as JSON: /api/errors/629cb4fff035058d. Report an issue: GitHub.