babalae/better-genshin-impact · error · Exception

读取文件失败: {ex.Message}

Error message

读取文件失败: {ex.Message}

What it means

Thrown by FileAccessBridge.ReadFile's catch-all: any exception inside ReadFile (UnauthorizedAccessException, FileNotFoundException, IOException, etc.) is wrapped and re-thrown as a generic Exception with prefix '读取文件失败: '. This destroys the original exception type and stack origin.

Source

Thrown at BetterGenshinImpact/Core/Script/WebView/FileAccessBridge.cs:71

        }
    }

    public string ReadFile(string relativePath)
    {
        try
        {
            var fullPath = Path.Combine(_allowedDirectory, relativePath);
            if (!IsPathAllowed(fullPath))
                throw new UnauthorizedAccessException($"访问路径 '{relativePath}' 被拒绝");

            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"文件 '{relativePath}' 不存在");

            return File.ReadAllText(fullPath, Encoding.UTF8);
        }
        catch (Exception ex)
        {
            throw new Exception($"读取文件失败: {ex.Message}");
        }
    }

    public void WriteFile(string relativePath, string content)
    {
        try
        {
            var fullPath = Path.Combine(_allowedDirectory, relativePath);
            if (!IsPathAllowed(fullPath))
                throw new UnauthorizedAccessException($"访问路径 '{relativePath}' 被拒绝");

            // var directory = Path.GetDirectoryName(fullPath);
            // if (!string.IsNullOrEmpty(directory))
            //     Directory.CreateDirectory(directory);

            File.WriteAllText(fullPath, content, new UTF8Encoding(false));
        }
        catch (Exception ex)

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Remove the catch-all rewrap; let the specific exception (UnauthorizedAccessException, FileNotFoundException, IOException) propagate so callers can handle by type.
  2. If a uniform exception type is required, use a custom exception type that preserves the original as InnerException and rethrow with ExceptionDispatchInfo.Capture(ex).Throw() to keep the stack.
  3. At minimum, log the full exception before rewrapping.

Example fix

// before
public string ReadFile(string relativePath)
{
    try { /* ... */ }
    catch (Exception ex) { throw new Exception($"读取文件失败: {ex.Message}"); }
}

// after (let specific exceptions propagate; only wrap unexpected IO errors preserving InnerException)
public string ReadFile(string relativePath)
{
    var fullPath = Path.Combine(_allowedDirectory, relativePath);
    if (!IsPathAllowed(fullPath)) throw new UnauthorizedAccessException($"访问路径 '{relativePath}' 被拒绝");
    if (!File.Exists(fullPath)) throw new FileNotFoundException($"文件 '{relativePath}' 不存在", fullPath);
    return File.ReadAllText(fullPath, Encoding.UTF8); // IOException propagates as-is
}
Defensive patterns

Strategy: try-catch

Try / catch

// Remove the catch-all; let specific exceptions propagate:
// - UnauthorizedAccessException (sandbox)
// - FileNotFoundException (missing)
// - IOException (locked/disk)
// If you must wrap, preserve InnerException:
catch (Exception ex)
{
    throw new FileAccessBridgeException("读取文件失败", ex);
}

Prevention

When it happens

Trigger: Any exception in the try block of ReadFile — sandbox denial (135), missing file (136), IO error, encoding error, or File.ReadAllText failure — is caught by `catch (Exception ex)` and rewrapped.

Common situations: The underlying error is one of 135/136 or an IOException (locked file, disk error); the rewrap hides the original type so callers cannot distinguish UnauthorizedAccessException from FileNotFoundException.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/2ccbbc6473ab87ef. Report an issue: GitHub.