microg/GmsCore · error · RequestHandlingException
DATA_ERR
DATA_ERR
Error message
The request options are not valid
What it means
The RequestOptions.registerOptions extension unwraps a WebAuthn/FIDO registration request into PublicKeyCredentialCreationOptions. It only accepts BrowserPublicKeyCredentialCreationOptions (unwrapping its inner options) or a plain PublicKeyCredentialCreationOptions; anything else throws RequestHandlingException with code DATA_ERR and message 'The request options are not valid'. This rejects mismatched option types (e.g. a sign/get request passed where a create/register request is expected).
Source
Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/RequestHandling.kt:42
import org.microg.gms.fido.core.transport.Transport
import org.microg.gms.utils.*
import java.net.HttpURLConnection
import java.security.MessageDigest
private const val TAG = "Fido"
class RequestHandlingException(val errorCode: ErrorCode, message: String? = null) : Exception(message)
class MissingPinException(message: String? = null): Exception(message)
class WrongPinException(message: String? = null): Exception(message)
data class CredentialUserInfo(val credential: String, val userJson: String, val transport: Transport, val timestamp: Long)
enum class RequestOptionsType { REGISTER, SIGN }
val RequestOptions.registerOptions: PublicKeyCredentialCreationOptions
get() = when (this) {
is BrowserPublicKeyCredentialCreationOptions -> publicKeyCredentialCreationOptions
is PublicKeyCredentialCreationOptions -> this
else -> throw RequestHandlingException(DATA_ERR, "The request options are not valid")
}
val RequestOptions.signOptions: PublicKeyCredentialRequestOptions
get() = when (this) {
is BrowserPublicKeyCredentialRequestOptions -> publicKeyCredentialRequestOptions
is PublicKeyCredentialRequestOptions -> this
else -> throw RequestHandlingException(DATA_ERR, "The request options are not valid")
}
val RequestOptions.type: RequestOptionsType
get() = when (this) {
is PublicKeyCredentialCreationOptions, is BrowserPublicKeyCredentialCreationOptions -> REGISTER
is PublicKeyCredentialRequestOptions, is BrowserPublicKeyCredentialRequestOptions -> SIGN
else -> throw RequestHandlingException(INVALID_STATE_ERR)
}
val RequestOptions.webAuthnType: String
get() = when (type) {View on GitHub (pinned to 157c9d86ac)
Solutions
- Pass PublicKeyCredentialCreationOptions or BrowserPublicKeyCredentialCreationOptions to the registration API
- Inspect the actual runtime class of the RequestOptions before use (options is PublicKeyCredentialCreationOptions) and route register vs sign accordingly
- If options come from an intent/JSON payload, fix deserialization so registration requests map to the creation-options class
- Catch RequestHandlingException and map DATA_ERR to a clear client error explaining the options type was wrong
Example fix
// before
val creation = options.registerOptions // throws DATA_ERR if options is a sign request
// after
val creation = when (options) {
is BrowserPublicKeyCredentialCreationOptions -> options.publicKeyCredentialCreationOptions
is PublicKeyCredentialCreationOptions -> options
else -> return Result.failure(RequestHandlingException(DATA_ERR, "Expected registration options"))
} Defensive patterns
Strategy: type-guard
Validate before calling
when (options) {
is BrowserPublicKeyCredentialCreationOptions, is PublicKeyCredentialCreationOptions -> handleRegister(options)
else -> failWith(DATA_ERR, "registration options required")
} Type guard
fun RequestOptions.isRegistrationOptions(): Boolean =
this is BrowserPublicKeyCredentialCreationOptions || this is PublicKeyCredentialCreationOptions Try / catch
try {
val creation = options.registerOptions
} catch (e: RequestHandlingException) {
if (e.code == DATA_ERR) return badRequest("expected registration options")
throw e
} Prevention
- Route register vs sign by RequestOptions.type before unwrapping
- Never reuse one options object across both WebAuthn ceremonies
- Verify deserialization maps registration payloads to creation-options classes
When it happens
Trigger: Passing a PublicKeyCredentialRequestOptions, BrowserPublicKeyCredentialRequestOptions, or any other RequestOptions subtype to code that reads .registerOptions (the FIDO register/create pipeline).
Common situations: Client code confusing the two WebAuthn ceremonies — calling navigator.credentials.get() options through the register path or vice versa; deserializing option bundles into the wrong class; third-party intents whose extras do not parse into either registration options class.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- NOT_ALLOWED_ERR
- Invalid result
- Transport ${transport} not supported
- Attachment ${attachment} not supported
- Attestation conveyance preference ${attachment} not supporte
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/2fdead9ee6bcbfd7.
Report an issue: GitHub.