skylot/jadx · error · JadxRuntimeException

Unexpected visibility flag:

Error message

Unexpected visibility flag: 

What it means

Thrown by AccessInfo.orderedVisibility, which orders the four legal JVM visibility flags. The switch accepts only AccessFlags.PRIVATE, 0 (package-private), AccessFlags.PROTECTED, AccessFlags.PUBLIC; any other int (e.g. a combined mask like PUBLIC|PROTECTED, or a non-visibility bit) hits the default. It enforces that visibility comparisons see exactly one canonical flag.

Source

Thrown at jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java:87

		int otherVis = otherAccInfo.accFlags & VISIBILITY_FLAGS;
		if (thisVis == otherVis) {
			return false;
		}
		return orderedVisibility(thisVis) < orderedVisibility(otherVis);
	}

	private static int orderedVisibility(int flag) {
		switch (flag) {
			case AccessFlags.PRIVATE:
				return 1;
			case 0: // package-private
				return 2;
			case AccessFlags.PROTECTED:
				return 3;
			case AccessFlags.PUBLIC:
				return 4;
			default:
				throw new JadxRuntimeException("Unexpected visibility flag: " + flag);
		}
	}

	public boolean isPublic() {
		return (accFlags & AccessFlags.PUBLIC) != 0;
	}

	public boolean isProtected() {
		return (accFlags & AccessFlags.PROTECTED) != 0;
	}

	public boolean isPrivate() {
		return (accFlags & AccessFlags.PRIVATE) != 0;
	}

	public boolean isPackagePrivate() {
		return (accFlags & VISIBILITY_FLAGS) == 0;
	}

View on GitHub (pinned to e738a26571)

Solutions

  1. Mask to the visibility bits before calling orderedVisibility: use AccessFlags.visibilityMask or AND with PUBLIC|PROTECTED|PRIVATE.
  2. Ensure exactly one visibility bit is set; if multiple, pick a precedence (PRIVATE < default < PROTECTED < PUBLIC) before ordering.
  3. Validate/normalise access flags at load time (in DexLoader/ClassNode) so downstream always sees a canonical value.
  4. Report malformed input upstream if the bits genuinely conflict.

Example fix

// before
switch (flag) {
    case AccessFlags.PRIVATE: return 1;
    case 0: return 2;
    case AccessFlags.PROTECTED: return 3;
    case AccessFlags.PUBLIC: return 4;
    default: throw new JadxRuntimeException("Unexpected visibility flag: " + flag);
}

// after (normalise conflicting masks to a canonical precedence)
if ((flag & AccessFlags.PUBLIC) != 0) return 4;
if ((flag & AccessFlags.PROTECTED) != 0) return 3;
if ((flag & AccessFlags.PRIVATE) != 0) return 1;
return 2; // package-private
Defensive patterns

Strategy: validation

Validate before calling

int vis = accFlags & (AccessFlags.PUBLIC | AccessFlags.PROTECTED | AccessFlags.PRIVATE);
int count = Integer.bitCount(vis);
if (count != 1 && vis != 0) {
    // conflicting bits - normalise before ordering
    vis = normaliseVisibility(accFlags);
}

Type guard

static boolean isCanonicalVisibility(int flag) {
    return flag == AccessFlags.PUBLIC || flag == AccessFlags.PROTECTED
        || flag == AccessFlags.PRIVATE || flag == 0;
}

Try / catch

int order;
try {
    order = AccessInfo.orderedVisibility(flag);
} catch (JadxRuntimeException e) {
    order = defaultVisibilityOrder(flag);
}

Prevention

When it happens

Trigger: orderedVisibility is called with a mask containing more than one visibility bit set, or a visibility mask that includes non-visibility bits, or a fully-zero-visibility-but-non-canonical value. Reachable from AccessInfo's visibility-sorting/comparison logic on access flags that violate the JVM invariant of exactly one visibility flag.

Common situations: Malformed bytecode with illegal access flag combinations; obfuscators that set conflicting visibility bits; manually-crafted DEX; bugs in flag extraction that pass the raw accFlags instead of just the visibility portion.

Related errors


AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14). Data as JSON: /api/errors/fe1a474bca87bf46. Report an issue: GitHub.