microg/GmsCore · error · RuntimeException

Failed to read license

Error message

Failed to read license

What it means

getLicenseText skips license.getOffset() bytes into the packaged third_party_licenses raw resource; if InputStream.skip() returns fewer bytes than requested, the offset is unreachable (file shorter or truncated) and a RuntimeException 'Failed to read license' is thrown. IOException during skip is wrapped in the same error.

Source

Thrown at play-services-oss-licenses/src/main/java/org/microg/gms/oss/licenses/LicenseUtil.java:61

        List<License> licenses = new ArrayList<>(lines.length);
        for (String line : lines) {
            int spaceIndex = line.indexOf(' ');
            String[] position = line.substring(0, spaceIndex).split(":");
            if (spaceIndex <= 0 || position.length != 2) {
                throw new IllegalStateException("Invalid license meta-data line:\n" + line);
            }
            licenses.add(new License(line.substring(spaceIndex + 1), Long.parseLong(position[0]), Integer.parseInt(position[1]), ""));
        }
        return licenses;
    }

    public static String getLicenseText(Context context, License license) {
        if (license.getPath().isEmpty()) {
            Resources resources = context.getApplicationContext().getResources();
            InputStream is = resources.openRawResource(resources.getIdentifier("third_party_licenses", "raw", context.getPackageName()));
            try {
                if (is.skip(license.getOffset()) != license.getOffset()) {
                    throw new RuntimeException("Failed to read license");
                }
            } catch (IOException e) {
                throw new RuntimeException("Failed to read license", e);
            }
            return readStringAndClose(is, license.getLength());
        } else {
            try (JarFile jar = new JarFile(license.getPath())) {
                JarEntry entry = jar.getJarEntry("res/raw/third_party_licenses");
                if (entry == null) {
                    throw new RuntimeException(license.getPath() + " does not contain res/raw/third_party_licenses");
                } else {
                    InputStream is = jar.getInputStream(entry);
                    if (is.skip(license.getOffset()) != license.getOffset()) {
                        throw new RuntimeException("Failed to read license");
                    }
                    return readStringAndClose(is, license.getLength());
                }
            } catch (IOException e) {

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Regenerate the third_party_licenses raw resource and its metadata together so offsets match
  2. Verify res/raw/third_party_licenses exists and contains the license text at the given offset
  3. Check that resource shrinking/minification is not removing or renaming the raw resource

Example fix

// no code fix at call site; regenerate resources so offsets align
// before: metadata offset 9000 but third_party_licenses file is only 5000 bytes
// after: rebuild license bundle so offsets are within file size
Defensive patterns

Strategy: try-catch

Validate before calling

// Check the raw resource is at least offset+length bytes before reading
Resources res = context.getResources();
int id = res.getIdentifier("third_party_licenses", "raw", context.getPackageName());
if (id == 0) throw new IllegalStateException("third_party_licenses raw resource missing");
try (InputStream is = res.openRawResource(id)) {
    if (is.skip(license.getOffset()) != license.getOffset()) {
        throw new IllegalStateException("Offset " + license.getOffset() + " beyond resource");
    }
}

Try / catch

try {
    String text = LicenseUtil.getLicenseText(context, license);
} catch (RuntimeException e) {
    Log.e(TAG, "Could not read license " + license.getTitle() + ": " + e.getMessage());
    text = "";
}

Prevention

When it happens

Trigger: Calling getLicenseText for a License whose offset exceeds the size of the res/raw/third_party_licenses resource, or when openRawResource/getIdentifier returned a mismatched resource; also any IOException raised while skipping.

Common situations: The licenses index/metadata was regenerated but the raw resource file is stale (offsets point past the end); resource name 'third_party_licenses' overridden or missing in the app's package; a shrinker stripped or altered the raw resource.

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 microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/5e5b645452f0c239. Report an issue: GitHub.