matryer/xbar · error

json.Unmarshal

Error message

json.Unmarshal

What it means

LoadVariableValues (pkg/plugins/variables.go:50-52) wraps json.Unmarshal failures with 'json.Unmarshal'. The .vars.json content is not a valid JSON object (JSON body must decode into map[string]interface{}), so the plugin's saved variables cannot be loaded. The wrapped error includes the exact byte offset and parse problem.

Source

Thrown at pkg/plugins/variables.go:52

func LoadVariableValues(pluginDir, installedPluginPath string) (map[string]interface{}, error) {
	filename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt)
	f, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			// no file - but not an error, just empty map
			return map[string]interface{}{}, nil
		}
		return nil, errors.Wrap(err, "Open")
	}
	defer f.Close()
	b, err := io.ReadAll(io.LimitReader(f, 1_000_000 /* ~1MB */))
	if err != nil {
		return nil, errors.Wrap(err, "ReadAll")
	}
	var values map[string]interface{}
	err = json.Unmarshal(b, &values)
	if err != nil {
		return nil, errors.Wrap(err, "json.Unmarshal")
	}
	return values, nil
}

func (p *Plugin) loadVariablesAsEnvVars() ([]string, error) {
	vars, err := p.loadVariables()
	if err != nil {
		return nil, errors.Wrap(err, "loadVariables")
	}
	envvars := make([]string, 0, len(vars))
	for k, v := range vars {
		envvars = append(envvars, fmt.Sprintf("%s=%v", k, v))
	}
	return envvars, nil
}

func (p *Plugin) loadVariables() (map[string]interface{}, error) {
	var wg sync.WaitGroup

View on GitHub (pinned to d624239058)

Solutions

  1. Open the .vars.json file in a JSON validator/linter and fix the syntax error (commonly trailing commas or missing quotes).
  2. Delete the corrupt file so LoadVariableValues returns an empty map and re-save variables from the plugin settings UI.
  3. Restore the file from backup if it was truncated by an interrupted write.
  4. Ensure the value saved is a JSON object (map), not an array or scalar.
  5. Upgrade code paths that write the file to write to a temp file and rename atomically to avoid corruption.

Example fix

// before — in-place write, vulnerable to truncation
ioutil.WriteFile(filename, b, 0666)
// after — atomic write
if err := ioutil.WriteFile(filename+".tmp", b, 0666); err != nil {
	return errors.Wrap(err, "WriteFile")
}
if err := os.Rename(filename+".tmp", filename); err != nil {
	return errors.Wrap(err, "Rename")
}
Defensive patterns

Strategy: validation

Validate before calling

path := filepath.Join(pluginDir, installedPluginPath+".vars.json")
b, err := os.ReadFile(path)
if err == nil {
	var v map[string]interface{}
	if err := json.Unmarshal(b, &v); err != nil {
		log.Printf("vars file corrupt, will reset: %v", err)
		os.Remove(path) // let the app recreate it
	}
}

Try / catch

values, err := plugins.LoadVariableValues(pluginDir, installedPluginPath)
if err != nil && strings.Contains(err.Error(), "json.Unmarshal") {
	log.Printf("corrupt vars file, resetting: %v", err)
	os.Remove(filepath.Join(pluginDir, installedPluginPath+".vars.json"))
	values = map[string]interface{}{}
}

Prevention

When it happens

Trigger: The vars file was hand-edited with invalid JSON; a previous save was interrupted/corrupted (partial write, disk full); the file contains an array, string, or null instead of an object; encoding issues (BOM, non-UTF8 bytes).

Common situations: Users edit the .vars.json file with a text editor and leave a trailing comma or quotes mismatched; crash during WriteFile leaves a truncated file; another tool writes a different format to the same path.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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