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
- Regenerate the third_party_licenses raw resource and its metadata together so offsets match
- Verify res/raw/third_party_licenses exists and contains the license text at the given offset
- 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
- Regenerate metadata and raw license text in the same build so offsets stay in sync
- Verify resource shrinking does not strip res/raw/third_party_licenses
- Check that getIdentifier resolves the resource in the app (not library) package
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
- Invalid license meta-data line:
- Could not read from parcel file descriptor
- Cannot advance the iterator beyond
- Cannot remove elements from a DataBufferIterator
- Must set attenuationBucketThresholdDb
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/5e5b645452f0c239.
Report an issue: GitHub.