Troubleshooting

Common errors, their causes, and how to fix them.

NotInitializedException

Message: Authyra has not been initialized. Call Authyra.initialize() before accessing Authyra.instance.

Cause: Authyra.instance was accessed before Authyra.initialize() completed.

Fix: ensure initialize is awaited before runApp:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Authyra.initialize(client: client); // must be awaited
  runApp(const MyApp());
}

Also check that Authyra.instance isn't accessed at module level (top-level variable initializers run before main).


ProviderNotFoundException

Message: No provider registered with id 'google'.

Cause: signIn('google', ...) was called but no provider with id == 'google' was registered with AuthyraClient.

Fix: confirm the provider is in the providers list and the id matches exactly:

// Provider
GoogleProvider(clientId: '...')   // id is 'google'

// Sign-in call
await Authyra.instance.signIn('google'); // matches ✓

AuthenticationFailedException

Message: Provider "email" returned null — credentials may be invalid.

Cause: your authorize callback returned null. This is the expected way to signal wrong credentials.

Fix in the callback: return null only for invalid credentials; throw for infrastructure errors:

authorize: (creds) async {
  final res = await myApi.post('/auth/login', body: creds);

  if (res.statusCode == 401) return null;          // wrong password → null
  if (res.statusCode != 200) {
    throw AuthenticationFailedException(            // server error → throw
      'Login failed: HTTP ${res.statusCode}',
      providerName: 'email',
    );
  }

  return AuthSignInResult(user: AuthUser(...), ...);
},

Fix in the caller: always catch the exception in your UI:

try {
  await Authyra.instance.signIn('email', params: {...});
} on AuthenticationFailedException {
  setState(() => _error = 'Invalid email or password.');
}

OAuth2 callback never arrives

Symptom: sign-in with Google/GitHub hangs; the browser opens but the app never receives the callback.

Checklist:

  1. Deep-link scheme registered — verify the URI scheme is in AndroidManifest.xml (Android) and Info.plist (iOS). See Flutter Setup →.
  2. Provider registered with OAuth2CallbackHandler — confirm registerProvider is called with the matching scheme prefix before Authyra.initialize:
    OAuth2CallbackHandler.registerProvider(
      'com.googleusercontent.apps.YOUR_ID', // must match redirect URI scheme
      googleProvider,
    );
    
  3. uriLinkStream forwarded — confirm incoming URIs reach the handler:
    AppLinks().uriLinkStream.listen(OAuth2CallbackHandler.handleCallback);
    
  4. Redirect URI matches — the URI registered in the OAuth console must exactly match OAuth2Config.redirectUri (including scheme and path).
  5. Timeout — by default OAuth2Config.timeout is 5 minutes. If the user never completes auth, AuthenticationCancelledException is thrown.

StorageException on initialize

Message: StorageException: Failed to initialize storage.

Cause: the AuthStorage implementation threw during initialize().

Common causes for SecureAuthStorage:

  • Android: missing minSdkVersion 18 in android/app/build.gradle
  • iOS: missing Keychain entitlement — add Keychain Sharing capability in Xcode
  • Web: running in a context without localStorage access (e.g., private browsing with strict settings)

TokenExpiredException

Message: Token for user X has expired.

Cause: the session's expiresAt is in the past and the provider does not support refresh (or the refresh token itself is expired).

Fix:

  • Enable AuthConfig(autoRefresh: true) to let Authyra refresh automatically.
  • Catch TokenExpiredException and send the user back to the login screen:
    try {
      final token = await Authyra.instance.getAccessToken();
    } on TokenExpiredException {
      await Authyra.instance.signOut();
    }
    

ProviderAlreadyRegisteredException

Message: A provider with id 'email' is already registered.

Cause: two providers with the same id were added to AuthyraClient, or registerProvider was called twice with the same id.

Fix: ensure each provider has a unique id. If you're dynamically adding providers, guard with a check:

if (!client.providers.any((p) => p.id == 'email')) {
  client.registerProvider(myEmailProvider);
}

InMemoryStorage doesn't persist across restarts

Expected behaviourInMemoryStorage is intentionally non-persistent. It's designed for tests and development.

Use SecureAuthStorage (Flutter) or your own AuthStorage implementation (backend) for production.


Tests fail with "singleton already initialized"

Cause: Authyra.instance was initialized in one test and not disposed before the next test ran.

Fix: dispose the singleton in tearDown:

setUp(() async {
  client = AuthyraClient(providers: [...], storage: InMemoryStorage());
  await Authyra.initialize(client: client);
});

tearDown(() async {
  if (Authyra.isInitialized) await Authyra.instance.dispose();
});

Alternatively, use AuthyraClient directly in tests — it has no singleton and requires no teardown:

setUp(() async {
  client = AuthyraClient(providers: [...], storage: InMemoryStorage());
  await client.initialize();
});

Still stuck?

Copyright © 2026