Resolving Common Issues When Upgrading to v1.34.2+ Architecture
This guide addresses the most common issues encountered when migrating from legacy Supabase UE5 Plugin versions to the new production-ready subsystem architecture. Each issue includes symptoms, root causes, and step-by-step solutions.
// Error logs
LogTemp: Error: SupabaseManager::LoginWithEmail: Subsystem not available
LogTemp: Error: GetSupabaseSubsystem returned nullptr
The subsystem is not properly initialized or you’re calling from an invalid world context.
// ❌ Bad - Invalid context
void UMyWidget::LoginUser()
{
// Widget may not have valid world context
USupabaseManager::LoginWithEmail(this, Email, Password);
}
// ✅ Good - Use proper world context
void UMyWidget::LoginUser()
{
if (UWorld* World = GetWorld())
{
USupabaseManager::LoginWithEmail(World, Email, Password);
}
else if (GetOwningPlayer())
{
USupabaseManager::LoginWithEmail(GetOwningPlayer(), Email, Password);
}
}
// ✅ Ensure subsystem is initialized
void AMyGameInstance::BeginPlay()
{
Super::BeginPlay();
// Initialize subsystem FIRST
USupabaseConnection* Connection = NewObject<USupabaseConnection>();
Connection->SupabaseServerUrl = TEXT("https://your-project.supabase.co");
Connection->SupabaseCredentials.AnonymousKey = TEXT("your-anon-key");
if (!USupabaseManager::InitializeSupabase(this, Connection))
{
UE_LOG(LogTemp, Error, TEXT("Failed to initialize Supabase"));
}
}
// ✅ Always check before using
void AMyActor::PerformSupabaseOperation()
{
if (!USupabaseManager::IsSubsystemAvailable(this))
{
UE_LOG(LogTemp, Error, TEXT("Supabase subsystem not available"));
return;
}
// Safe to proceed
USupabaseManager::TestConnection(this);
}
// Compilation errors
Error: No matching function for call to 'UAsyncLogin::UAsyncLogin'
Error: Cannot convert from 'TSharedPtr<IHttpRequest>' to 'TSharedRef<IHttpRequest>'
Error: 'SafeCleanup' is not a member of 'UAsyncLogin'
Legacy async operation signatures have changed in the new architecture.
// ❌ Old pattern - will not compile
UAsyncLogin* LoginNode = NewObject<UAsyncLogin>();
LoginNode->Connection = MyConnection;
LoginNode->Email = UserEmail;
LoginNode->Password = UserPassword;
// ✅ New pattern - subsystem integration
UAsyncLogin* LoginNode = UAsyncLogin::LoginWithEmailAsync(
this, // World context
UserEmail, // Email
UserPassword // Password
// No connection needed - uses subsystem
);
// ❌ Compilation error
void SetupRequest()
{
SetupRequestHeaders(CurrentRequest); // TSharedPtr passed to TSharedRef parameter
}
// ✅ Fixed - convert to TSharedRef
void SetupRequest()
{
if (CurrentRequest.IsValid())
{
SetupRequestHeaders(CurrentRequest.ToSharedRef());
}
}
// ❌ Old delegate signature - may cause binding issues
UFUNCTION()
void OnLoginSuccess(FString Response);
// ✅ New delegate signature - matches subsystem events
UFUNCTION()
void OnLoginSuccess(FString Response, const FTokenResponse& TokenResponse);
// Memory leak warnings
LogTemp: Warning: Async operation not properly cleaned up
LogTemp: Error: HTTP request still active during shutdown
// Crashes
Assertion failed: !IsValid() [UAsyncLogin::BeginDestroy]
Access violation reading location 0x0000000000000000
Legacy async operations don’t implement proper cleanup mechanisms.
// ❌ Storing async operations causes memory leaks
UPROPERTY()
UAsyncLogin* StoredLoginNode;
void StartLogin()
{
StoredLoginNode = UAsyncLogin::LoginWithEmailAsync(this, Email, Password);
// Node never gets cleaned up properly
}
// ✅ Let async operations manage themselves
void StartLogin()
{
UAsyncLogin* LoginNode = UAsyncLogin::LoginWithEmailAsync(this, Email, Password);
LoginNode->OnSuccess.AddDynamic(this, &AMyClass::OnLoginSuccess);
// Node automatically cleans up after completion
}
// ✅ If you must store references, implement cleanup
UPROPERTY()
TArray<UAsyncLogin*> ActiveOperations;
void StartLogin()
{
UAsyncLogin* LoginNode = UAsyncLogin::LoginWithEmailAsync(this, Email, Password);
ActiveOperations.Add(LoginNode);
LoginNode->OnSuccess.AddDynamic(this, &AMyClass::OnLoginSuccess);
LoginNode->OnFailure.AddDynamic(this, &AMyClass::OnLoginFailure);
}
void BeginDestroy()
{
// Clean up active operations
for (UAsyncLogin* Operation : ActiveOperations)
{
if (IsValid(Operation))
{
Operation->SafeCleanup();
}
}
ActiveOperations.Empty();
Super::BeginDestroy();
}
// User appears logged in but operations fail
LogTemp: Warning: User authenticated but requests return 401
LogTemp: Error: Token expired but refresh failed
LogTemp: Log: Connection state inconsistent across operations
Multiple client instances with different authentication states.
// ❌ Multiple client instances lose sync
void AMyPlayerController::Login()
{
USupabaseClient* ClientA = NewObject<USupabaseClient>(); // Instance 1
ClientA->LoginWithEmail(Email, Password);
}
void AMyGameMode::QueryData()
{
USupabaseClient* ClientB = NewObject<USupabaseClient>(); // Instance 2 - different auth state
ClientB->QueryTable(TEXT("users"), TEXT("*"));
}
// ✅ Single subsystem maintains consistent state
void AMyPlayerController::Login()
{
USupabaseManager::LoginWithEmail(this, Email, Password);
}
void AMyGameMode::QueryData()
{
// Uses same subsystem instance with consistent auth state
UAsyncQuery* QueryNode = UAsyncQuery::QueryTableAsync(this, TEXT("users"), TEXT("*"));
}
// ✅ Monitor authentication state changes
void AMyGameInstance::BeginPlay()
{
Super::BeginPlay();
USupabaseSubsystem* Subsystem = GetSubsystem<USupabaseSubsystem>();
if (Subsystem)
{
// Bind to auth state changes
Subsystem->OnAuthenticationStateChanged.AddDynamic(
this, &AMyGameInstance::OnAuthStateChanged);
}
}
UFUNCTION()
void AMyGameInstance::OnAuthStateChanged(bool bIsAuthenticated, const FUser& User)
{
if (!bIsAuthenticated)
{
UE_LOG(LogTemp, Warning, TEXT("User authentication lost - redirecting to login"));
// Handle auth loss appropriately
}
}
Blueprint compilation error: Node 'Create Supabase Client' not found
Blueprint runtime error: Attempted to access null object reference
Blueprint warning: Pins have been orphaned
Old Blueprint nodes are no longer valid after architecture changes.
// ❌ Old nodes - will cause errors
Create Supabase Client -> Set Supabase Connection -> Login With Email
// ✅ New nodes - use Manager pattern
Initialize Supabase (Manager) -> Login With Email (Manager)
// ✅ Consistent Manager usage
Event BeginPlay
-> Create Supabase Connection
-> Initialize Supabase (Manager)
-> Branch (Success?)
-> True: Is Connected (Manager)
-> Login With Email (Manager)
-> False: Print String "Initialization Failed"
// ❌ Old event binding - will fail
SupabaseClient -> Bind Event to OnLoginSuccessful
// ✅ New event binding - use subsystem
Get Supabase Subsystem -> Bind Event to OnLoginSuccessful
LogTemp: Error: Connection timeout after 30.0 seconds
LogTemp: Warning: Supabase server unreachable
LogTemp: Error: Failed to establish WebSocket connection
// Step 1: Validate connection configuration
void DiagnoseConnection()
{
USupabaseConnection* Connection = USupabaseManager::GetActiveConnection(this);
if (!Connection)
{
UE_LOG(LogTemp, Error, TEXT("No active connection"));
return;
}
UE_LOG(LogTemp, Log, TEXT("Server URL: %s"), *Connection->SupabaseServerUrl);
UE_LOG(LogTemp, Log, TEXT("Anonymous Key: %s"),
!Connection->SupabaseCredentials.AnonymousKey.IsEmpty() ? TEXT("Set") : TEXT("Empty"));
}
// Step 2: Test network connectivity
void TestNetworkConnectivity()
{
USupabaseManager::TestConnection(this);
// Check subsystem events
USupabaseSubsystem* Subsystem = USupabaseManager::GetSupabaseSubsystem(this);
if (Subsystem)
{
Subsystem->OnConnectionStateChanged.AddDynamic(this, &AMyClass::OnConnectionTest);
}
}
// Solution 1: Validate URL format
bool ValidateSupabaseURL(const FString& URL)
{
if (!URL.StartsWith(TEXT("https://")))
{
UE_LOG(LogTemp, Error, TEXT("Supabase URL must use HTTPS"));
return false;
}
if (!URL.Contains(TEXT(".supabase.co")))
{
UE_LOG(LogTemp, Warning, TEXT("URL doesn't appear to be a Supabase URL"));
}
return true;
}
// Solution 2: Enable debug logging
void EnableConnectionDebugging()
{
USupabaseManager::EnableDebugLogging(this, true);
// Will now log detailed connection information
USupabaseManager::TestConnection(this);
}
LogSupabaseRealtime: Error: WebSocket connection failed
LogSupabaseRealtime: Warning: Reconnection attempt 3/5 failed
LogSupabaseRealtime: Error: Channel subscription failed
Legacy real-time implementation lacks proper error handling and reconnection logic.
// ❌ Old pattern - unreliable
UAsyncRealtime* RealtimeNode = NewObject<UAsyncRealtime>();
RealtimeNode->Connection = MyConnection;
RealtimeNode->Table = TEXT("game_events");
// ✅ New pattern - with automatic reconnection
UAsyncRealtime* RealtimeNode = UAsyncRealtime::ListenToTable(
this, // World context
TEXT("game_events"), // Table
TEXT("public"), // Schema
TEXT("*") // Event types (all)
);
// Automatic reconnection and error recovery built-in
RealtimeNode->OnInsert.AddDynamic(this, &AMyClass::OnDataInserted);
RealtimeNode->OnConnectionStateChanged.AddDynamic(this, &AMyClass::OnRealtimeStateChanged);
UFUNCTION()
void AMyClass::OnRealtimeStateChanged(ERealtimeConnectionState State)
{
switch (State)
{
case ERealtimeConnectionState::Connecting:
UE_LOG(LogTemp, Log, TEXT("Real-time connecting..."));
break;
case ERealtimeConnectionState::Connected:
UE_LOG(LogTemp, Log, TEXT("Real-time connected"));
break;
case ERealtimeConnectionState::Subscribed:
UE_LOG(LogTemp, Log, TEXT("Real-time subscribed"));
break;
case ERealtimeConnectionState::Error:
UE_LOG(LogTemp, Error, TEXT("Real-time connection error"));
// Handle error appropriately
break;
case ERealtimeConnectionState::Reconnecting:
UE_LOG(LogTemp, Warning, TEXT("Real-time reconnecting..."));
break;
}
}
Error: Module 'Supabase' could not be loaded
Error: WebSockets module not found
Error: HTTP module dependencies missing
// ✅ Complete dependencies for new architecture
public class Supabase : ModuleRules
{
public Supabase(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(new string[] {
// Add public include paths
});
PrivateIncludePaths.AddRange(new string[] {
// Add private include paths
});
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"HTTP", // Required for HTTP requests
"Json", // Required for JSON parsing
"JsonUtilities", // Required for JSON utilities
"WebSockets", // Required for real-time
"WebBrowserWidget" // Required for OAuth (if used)
});
PrivateDependencyModuleNames.AddRange(new string[]
{
// Add private dependencies
});
DynamicallyLoadedModuleNames.AddRange(new string[]
{
// Add dynamically loaded modules
});
}
}
Error: 'USupabaseSubsystem.h' file not found
Error: 'SupabaseManager.h' file not found
Fatal error: 'Subsystems/GameInstanceSubsystem.h' file not found
// ✅ Correct includes for new architecture
#include "SupabaseManager.h" // For static access
#include "SupabaseSubsystem.h" // For direct subsystem access
#include "Subsystems/GameInstanceSubsystem.h" // For subsystem base class
#include "Engine/GameInstance.h" // For GameInstance access
#include "Engine/World.h" // For World context
LogTemp: Warning: Memory usage increased after migration
LogTemp: Error: Garbage collection not freeing async operations
// Check for stored async operation references
void DiagnoseMemoryUsage()
{
// Look for these patterns in your code:
UPROPERTY()
UAsyncLogin* StoredLoginNode; // ❌ This causes memory leaks
UPROPERTY()
TArray<UAsyncQuery*> QueryNodes; // ❌ This prevents cleanup
}
// ✅ Let async operations manage themselves
void PerformLogin()
{
UAsyncLogin* LoginNode = UAsyncLogin::LoginWithEmailAsync(this, Email, Password);
LoginNode->OnSuccess.AddDynamic(this, &AMyClass::OnLoginSuccess);
// Node automatically destroys itself after completion
}
// ✅ If you need to track operations, use weak references
UPROPERTY()
TArray<TWeakObjectPtr<UAsyncLogin>> WeakLoginNodes;
LogTemp: Warning: Multiple SupabaseClient instances detected
LogTemp: Error: Authentication state inconsistent
LogTemp: Log: Connection count: 5 (should be 1)
// ✅ Always use subsystem for operations
void CheckConnectionCount()
{
USupabaseSubsystem* Subsystem = USupabaseManager::GetSupabaseSubsystem(this);
if (Subsystem)
{
USupabaseClient* Client = Subsystem->GetSupabaseClient();
UE_LOG(LogTemp, Log, TEXT("Using centralized client: %p"), Client);
// Should always be the same client instance
}
}
// Debug utility for migration issues
void DiagnoseMigrationIssues()
{
UE_LOG(LogTemp, Log, TEXT("=== Migration Diagnostics ==="));
// Check subsystem
bool bSubsystemAvailable = USupabaseManager::IsSubsystemAvailable(this);
UE_LOG(LogTemp, Log, TEXT("Subsystem Available: %s"), bSubsystemAvailable ? TEXT("Yes") : TEXT("No"));
if (bSubsystemAvailable)
{
// Check connection
bool bConnected = USupabaseManager::IsConnected(this);
UE_LOG(LogTemp, Log, TEXT("Connected: %s"), bConnected ? TEXT("Yes") : TEXT("No"));
// Check authentication
bool bAuthenticated = USupabaseManager::IsAuthenticated(this);
UE_LOG(LogTemp, Log, TEXT("Authenticated: %s"), bAuthenticated ? TEXT("Yes") : TEXT("No"));
// Get status
FString Status = USupabaseManager::GetConnectionStatus(this);
UE_LOG(LogTemp, Log, TEXT("Status: %s"), *Status);
}
UE_LOG(LogTemp, Log, TEXT("========================"));
}
This troubleshooting guide covers the most common migration issues. If you encounter problems not covered here, please check the Discord community or create a GitHub issue with detailed logs.