{"record":{"id":"e8737507648aa3a0","repo":"charmbracelet/crush","slug":"error-reading-file-w-e87375","errorCode":null,"errorMessage":"error reading file: %w","messagePattern":"error reading file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/lsp/client.go","lineNumber":416,"sourceCode":"\treturn handlesFiletype(c.name, c.fileTypes, path)\n}\n\n// OpenFile opens a file in the LSP server.\nfunc (c *Client) OpenFile(ctx context.Context, filepath string) error {\n\tif !c.HandlesFile(filepath) {\n\t\treturn nil\n\t}\n\n\turi := string(protocol.URIFromPath(filepath))\n\n\tif _, exists := c.openFiles.Get(uri); exists {\n\t\treturn nil // Already open\n\t}\n\n\t// Skip files that do not exist or cannot be read\n\tcontent, err := os.ReadFile(filepath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error reading file: %w\", err)\n\t}\n\n\t// Notify the server about the opened document\n\tif err = c.client.NotifyDidOpenTextDocument(ctx, uri, string(powernap.DetectLanguage(filepath)), 1, string(content)); err != nil {\n\t\treturn err\n\t}\n\n\tc.openFiles.Set(uri, &OpenFileInfo{\n\t\tVersion: 1,\n\t\tURI:     protocol.DocumentURI(uri),\n\t})\n\n\treturn nil\n}\n\n// NotifyChange notifies the server about a file change.\nfunc (c *Client) NotifyChange(ctx context.Context, filepath string) error {\n\tif c == nil {","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/lsp/client.go#L398-L434","documentation":"OpenFile reads the target file from disk with os.ReadFile before sending a textDocument/didOpen notification. If the read fails (missing file, permission denied, I/O error), the error is wrapped as \"error reading file\". The client intentionally skips files that are already open, so this error fires only for files that exist in the caller's model but cannot actually be read.","triggerScenarios":"Calling OpenFile (directly or via Restart/OpenFileOnDemand/openKeyConfigFiles) with a filepath that does not exist, is a directory, lacks read permission, or was deleted between discovery and opening.","commonSituations":"Stale file paths after a branch switch or `git clean`; trying to open generated files that don't exist yet; opening files under a path requiring elevated permissions; racing with a build that deletes/recreates the file.","solutions":["Check os.Stat(filepath) before calling OpenFile and skip nonexistent paths.","Verify the path is a regular file (not a directory or symlink to nothing).","Fix file read permissions on the target path.","Re-resolve the file path if the workspace changed (branch switch, refactor)."],"exampleFix":"// before\nclient.OpenFile(ctx, cfgPath) // may not exist\n\n// after\nif _, err := os.Stat(cfgPath); err == nil {\n    client.OpenFile(ctx, cfgPath)\n}","handlingStrategy":"validation","validationCode":"info, err := os.Stat(filepath)\nif err != nil || info.IsDir() {\n    return nil // skip: unreadable or not a regular file\n}","typeGuard":"func readableFile(path string) bool {\n    info, err := os.Stat(path)\n    return err == nil && info.Mode().IsRegular()\n}","tryCatchPattern":"if err := client.OpenFile(ctx, path); err != nil {\n    if strings.Contains(err.Error(), \"error reading file\") {\n        slog.Warn(\"Skipping unreadable file for LSP\", \"path\", path, \"error\", err)\n        return nil\n    }\n    return err\n}","preventionTips":["Only open files you just listed from the filesystem, not cached paths.","Skip files under paths known to be generated or ephemeral.","Run crush with sufficient permissions for the workspace it indexes."],"tags":["lsp","file-io","didopen"],"backgroundTag":"file-not-found","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}