microg/GmsCore · error · IllegalArgumentException

rotationTimeHours is not in range of 0x1-0xffff

Error message

rotationTimeHours is not in range of 0x1-0xffff

What it means

ThreadNetworkCredentials.SecurityPolicy's constructor validates that rotationTimeHours is within the Thread-spec range 0x1-0xffff and throws IllegalArgumentException otherwise. The rotation time is a 16-bit unsigned field in the Thread network dataset, so 0 or values above 65535 are unrepresentable and invalid.

Source

Thrown at play-services-threadnetwork/src/main/java/com/google/android/gms/threadnetwork/ThreadNetworkCredentials.java:177

    }

    /**
     * The class represents Thread Security Policy.
     */
    public static class SecurityPolicy {
        private final int rotationTimeHours;
        private final byte[] flags;

        /**
         * Creates a new {@link SecurityPolicy} object.
         *
         * @param rotationTimeHours the value for Thread key rotation in hours. Must be in range of 0x1-0xffff.
         * @param flags             security policy flags with length of either 1 byte for Thread 1.1 or 2 bytes for Thread 1.2 or higher.
         * @throws IllegalArgumentException if {@code rotationTimeHours} is not in range of 0x1-0xffff or
         *                                  length of flags is smaller than {@link ThreadNetworkCredentials#LENGTH_MIN_SECURITY_POLICY_FLAGS}.
         */
        public SecurityPolicy(int rotationTimeHours, byte[] flags) {
            if (rotationTimeHours < 1 || rotationTimeHours > 0xffff) throw new IllegalArgumentException("rotationTimeHours is not in range of 0x1-0xffff");
            if (flags.length < LENGTH_MIN_SECURITY_POLICY_FLAGS) throw new IllegalArgumentException("length of flags is smaller than LENGTH_MIN_SECURITY_POLICY_FLAGS");
            this.rotationTimeHours = rotationTimeHours;
            this.flags = flags;
        }

        /**
         * Returns 1 byte flags for Thread 1.1 or 2 bytes flags for Thread 1.2.
         */
        public byte[] getFlags() {
            return flags;
        }

        /**
         * Returns the Security Policy Rotation Time in hours.
         */
        public int getRotationTimeHours() {
            return rotationTimeHours;
        }

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Use a value in [1, 65535]; the Thread default rotation time is 672 hours
  2. Validate the parsed value before constructing SecurityPolicy
  3. Replace 0 placeholder defaults with the spec default (672)
  4. If the source is unsigned 16-bit data, mask with & 0xFFFF and confirm it's nonzero

Example fix

// before
SecurityPolicy policy = new SecurityPolicy(0, flags); // IllegalArgumentException
// after
int rotationTimeHours = 672; // Thread default, must be 1..0xffff
SecurityPolicy policy = new SecurityPolicy(rotationTimeHours, flags);
Defensive patterns

Strategy: validation

Validate before calling

if (rotationTimeHours < 1 || rotationTimeHours > 0xffff) throw new IllegalArgumentException("rotationTimeHours must be in 0x1-0xffff, got " + rotationTimeHours);

Type guard

boolean isValidRotationTime(int hours) { return hours >= 1 && hours <= 0xffff; }

Try / catch

try {
    SecurityPolicy p = new SecurityPolicy(rotationTimeHours, flags);
} catch (IllegalArgumentException e) {
    // fall back to the Thread default of 672 hours
}

Prevention

When it happens

Trigger: Constructing a SecurityPolicy with rotationTimeHours == 0 or > 65535, e.g. from a dataset field parsed without range validation or a default of 0 used to mean 'unset'.

Common situations: Initializing rotationTimeHours to 0 as a placeholder; reading the value from a TLV parser that returns a wider int; config files with 0 meaning 'use default'.

Related errors


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/87ea0894b94188b9. Report an issue: GitHub.