{"record":{"id":"e73d300f932d8b7c","repo":"matryer/xbar","slug":"open-vars-json-file","errorCode":null,"errorMessage":"open vars json file","messagePattern":"open vars json file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/plugins/variables.go","lineNumber":107,"sourceCode":"\t}\n\t// add the json file vars to the defaults,\n\t// and return them.\n\tfor k, v := range jsonFileVars {\n\t\tdefaultVars[k] = v\n\t}\n\treturn defaultVars, nil\n}\n\n// loadVariablesFromJSONFile gets a list of environment variable friendly\n// key=value pairs.\nfunc (p *Plugin) loadVariablesFromJSONFile() (map[string]interface{}, error) {\n\tvariablesJSONFilename := p.Command + variableJSONFileExt\n\tf, err := os.Open(variablesJSONFilename)\n\tif err != nil && os.IsNotExist(err) {\n\t\t// no .vars.json file - no probs\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\treturn nil, errors.Wrap(err, \"open vars json file\")\n\t}\n\tdefer f.Close()\n\tb, err := io.ReadAll(io.LimitReader(f, 1_000_000))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar vars map[string]interface{}\n\tif err := json.Unmarshal(b, &vars); err != nil {\n\t\treturn nil, errors.Wrap(err, \"json.Unmarshal\")\n\t}\n\treturn vars, nil\n}\n\nfunc (p *Plugin) loadVariablesFromPluginMetadata() (map[string]interface{}, error) {\n\t// read the plugin metadata for default values\n\tpluginFile, err := os.Open(p.Command)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"open plugin source\")","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/matryer/xbar/blob/d624239058997c80118eaebe2e7f8331b3c765e0/pkg/plugins/variables.go#L89-L125","documentation":"loadVariablesFromJSONFile (pkg/plugins/variables.go:102-107) opens <plugin.Command>.vars.json. A missing file is deliberately ignored (returns nil, nil), but any other os.Open error is wrapped as 'open vars json file'. This means the vars file exists but is inaccessible — e.g. permission denied, path is a directory, or too many open files.","triggerScenarios":"Permission denied on the .vars.json or a parent directory; the path resolves to a directory; a broken symlink; EMFILE (too many open files) when many plugins load concurrently; I/O error on the underlying storage.","commonSituations":"xbar runs with many plugins simultaneously and hits the open-file limit; backup/sync placeholder directories named *.vars.json; file created by another user (root) with restrictive mode.","solutions":["Inspect the file: `ls -la <plugin>.vars.json` — check it is a regular file with read permission.","Fix permissions/ownership or remove a directory/placeholder at that path.","Raise the open-file limit (ulimit -n) if many plugins load at once.","Check audit logs / use errors %+v to see the exact errno behind the wrapper."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"varsPath := p.Command + \".vars.json\"\nif info, err := os.Stat(varsPath); err == nil {\n\tif !info.Mode().IsRegular() {\n\t\tlog.Printf(\"%s is not a regular file\", varsPath)\n\t}\n\tif info.Mode().Perm()&0400 == 0 {\n\t\tlog.Printf(\"%s not readable\", varsPath)\n\t}\n}","typeGuard":null,"tryCatchPattern":"vars, err := p.loadVariables()\nif err != nil {\n\tvar se syscall.Errno\n\tif errors.As(errors.Cause(err), &se) && errors.Is(se, syscall.EMFILE) {\n\t\tlog.Printf(\"too many open files while loading vars: %v\", err)\n\t}\n\treturn nil, err\n}","preventionTips":["Raise ulimit -n / RLIMIT_NOFILE if xbar runs many plugins concurrently.","Remove directory placeholders masquerading as .vars.json files.","Normalize ownership/permissions of the plugins directory after restores.","Audit broken symlinks in the plugin dir periodically."],"tags":["go","filesystem","file-open","permissions"],"backgroundTag":"file-open-permission-denied","analyzedSha":"d624239058997c80118eaebe2e7f8331b3c765e0","analyzedAt":"2026-09-02T22:38:22.007Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}