{"record":{"id":"39d4846be91994b9","repo":"microg/GmsCore","slug":"not-a-boolean-type","errorCode":null,"errorMessage":"Not a boolean type","messagePattern":"Not a boolean type","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Flag.java","lineNumber":77,"sourceCode":"    }\n\n    public Flag(String name, byte[] bytesValue, int flagType) {\n        this.name = name;\n        this.bytesValue = bytesValue;\n        this.dataType = DATA_TYPE_BYTES;\n        this.flagType = flagType;\n    }\n\n    public long getLong() {\n        if (dataType == DATA_TYPE_LONG)\n            return longValue;\n        throw new IllegalArgumentException(\"Not a long type\");\n    }\n\n    public boolean getBool() {\n        if (dataType == DATA_TYPE_BOOL)\n            return boolValue;\n        throw new IllegalArgumentException(\"Not a boolean type\");\n    }\n\n    public double getDouble() {\n        if (dataType == DATA_TYPE_DOUBLE)\n            return doubleValue;\n        throw new IllegalArgumentException(\"Not a double type\");\n    }\n\n    public String getString() {\n        if (dataType == DATA_TYPE_STRING)\n            return stringValue;\n        throw new IllegalArgumentException(\"Not a String type\");\n    }\n\n    public byte[] getBytes() {\n        if (dataType == DATA_TYPE_BYTES)\n            return bytesValue;\n        throw new IllegalArgumentException(\"Not a bytes type\");","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Flag.java#L59-L95","documentation":"Flag.getBool() throws IllegalArgumentException(\"Not a boolean type\") when the flag's dataType is not DATA_TYPE_BOOL. Flag is a tagged union of primitive types, and each accessor guards its field with the type discriminator. Reading the boolean field of a non-boolean flag is rejected rather than returning a bogus default.","triggerScenarios":"Calling Flag.getBool() on a Flag whose dataType is DATA_TYPE_LONG (1), DATA_TYPE_DOUBLE (3), DATA_TYPE_STRING (4), or DATA_TYPE_BYTES (5) instead of DATA_TYPE_BOOL (2).","commonSituations":"Assuming all experiment flags of a given name are booleans after a server-side type change; iterating flag collections without a type switch; restoring flags from a Bundle where the type byte was lost or mis-set.","solutions":["Guard with flag.getDataType() == Flag.DATA_TYPE_BOOL before calling getBool()","Switch over the DATA_TYPE_* constants and dispatch to the correct typed getter","Verify the server-side flag is still defined as a boolean experiment","Catch IllegalArgumentException at untrusted deserialization boundaries"],"exampleFix":"// before\nboolean enabled = flag.getBool();\n// after\nboolean enabled = flag.getDataType() == Flag.DATA_TYPE_BOOL && flag.getBool();","handlingStrategy":"type-guard","validationCode":"if (flag.getDataType() != Flag.DATA_TYPE_BOOL) { throw new IllegalStateException(\"flag is not a bool: \" + flag.getDataType()); }","typeGuard":"static boolean isBoolFlag(Flag f) { return f != null && f.getDataType() == Flag.DATA_TYPE_BOOL; }","tryCatchPattern":"try { boolean v = flag.getBool(); } catch (IllegalArgumentException e) { log.warn(\"Flag type mismatch\", e); v = DEFAULT_BOOL; }","preventionTips":["Check dataType == DATA_TYPE_BOOL before getBool()","Handle all DATA_TYPE_* constants in a single switch when processing flag lists","Re-check server flag definitions after they change type","Default safely instead of crashing on unexpected flag types"],"tags":["android","phenotype","type-mismatch","flags"],"backgroundTag":"type-mismatch","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}