Tencent/matrix · error · IOException

Can't read mapping file

Error message

Can't read mapping file

What it means

MappingReader.read() parses a ProGuard/R8 mapping.txt file line by line to enable stack-trace retrace. Any IOException encountered while reading the file (including from the wrapped BufferedReader) is caught and rethrown as a new IOException with the message "Can't read mapping file", preserving the original cause. This tells you the mapping file itself could not be read, not that its content was semantically wrong.

Solutions

  1. Check the underlying cause (err.getCause()) to see whether it is FileNotFoundException, permission denied, or another I/O failure.
  2. Verify the mapping file path passed to MappingReader exists and is a readable regular file before calling read().
  3. Ensure the file is not opened twice / not closed by another component before read() runs.
  4. In CI, confirm the mapping.txt artifact from the ProGuard/R8 task is produced and copied to the expected location.

Example fix

// before
MappingReader reader = new MappingReader(new File(mappingPath));
reader.read(sink);
// after
File f = new File(mappingPath);
if (!f.isFile() || !f.canRead()) {
    throw new IllegalStateException("mapping file missing or unreadable: " + mappingPath);
}
try {
    new MappingReader(f).read(sink);
} catch (IOException e) {
    e.printStackTrace(); // inspect getCause() for the real reason
}
Defensive patterns

Strategy: try-catch

Validate before calling

File f = new File(mappingPath);
if (f == null || !f.isFile() || !f.canRead() || f.length() == 0) {
    throw new IllegalStateException("mapping file missing/unreadable: " + mappingPath);
}

Try / catch

try {
    new MappingReader(mappingFile).read(sink);
} catch (IOException e) {
    Throwable cause = e.getCause();
    if (cause instanceof FileNotFoundException) {
        // fix path / regenerate mapping
    }
    log.warn("retrace skipped: mapping unreadable", e);
}

Prevention

When it happens

Trigger: Calling MappingReader.read() (typically from run()) when the mapping file path does not exist, is unreadable due to permissions, is already closed/corrupt, or the underlying stream hits an I/O error mid-parse (disk error, interrupted stream).

Common situations: Build/CI pipelines that pass a missing or stale mapping.txt path after a clean; retrace tools pointed at a renamed obfuscation output directory; reading the mapping from storage the process lacks permission for; transient disk or file-descriptor issues.

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 Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/0787c183a4162394. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/retrace/MappingReader.java:70

            while (true) {
                String line = reader.readLine();
                if (line == null) {
                    break;
                }
                line = line.trim();
                if (!line.startsWith("#")) {
                    // a class mapping
                    if (line.endsWith(SPLIT)) {
                        className = parseClassMapping(line, mappingProcessor);
                    } else if (className != null) { // a class member mapping
                        parseClassMemberMapping(className, line, mappingProcessor);
                    }
                } else {
                    Log.i(TAG, "comment:# %s", line);
                }
            }
        } catch (IOException err) {
            throw new IOException("Can't read mapping file", err);
        } finally {
            try {
                reader.close();
            } catch (IOException ex) {
                // do nothing
            }
        }
    }

    /**
     * @param line read content
     * @param mappingProcessor
     * @return
     */
    private String parseClassMapping(String line, MappingProcessor mappingProcessor) {

        int leftIndex = line.indexOf(ARROW);
        if (leftIndex < 0) {

View on GitHub (pinned to 3b8293bd65)