Why my pawn is not valid on the client controller

// Copyright Epic Games, Inc. All Rights Reserved.


#include "KartPlayerController.h"
#include "KartPawn.h"
#include "KartUI.h"
#include "EnhancedInputSubsystems.h"
#include "ChaosWheeledVehicleMovementComponent.h"

void AKartPlayerController::BeginPlay()
{
	Super::BeginPlay();
	
	// spawn the UI widget and add it to the viewport
	// check if it is not the server
	if (IsLocalController())
	{
		VehicleUI = CreateWidget<UKartUI>(this, VehicleUIClass);

		if (VehicleUI)
		{
			VehicleUI->AddToViewport();
		}
	}
}

void AKartPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();

	// get the enhanced input subsystem
	if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
	{
		// add the mapping context so we get controls
		Subsystem->AddMappingContext(InputMappingContext, 0);

		// optionally add the steering wheel context
		if (bUseSteeringWheelControls && SteeringWheelInputMappingContext)
		{
			Subsystem->AddMappingContext(SteeringWheelInputMappingContext, 1);
		}
	}
}

void AKartPlayerController::Tick(float Delta)
{
	Super::Tick(Delta);

	// print what role is this controller

	if (IsLocalController())
	{
		//VehiclePawn = Cast<AKartPawn>(GetPawn());
		UE_LOG(LogTemp, Warning, TEXT("Local Controller"));
		if (IsValid(VehiclePawn) && IsValid(VehicleUI))
		{
			VehicleUI->UpdateSpeed(VehiclePawn->GetChaosVehicleMovement()->GetForwardSpeed());
			VehicleUI->UpdateGear(VehiclePawn->GetChaosVehicleMovement()->GetCurrentGear());
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("VehiclePawn or VehicleUI is not valid"));
		}
	}
}

void AKartPlayerController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);

	// get a pointer to the controlled pawn
	VehiclePawn = CastChecked<AKartPawn>(InPawn);

	UE_LOG(LogTemp, Warning, TEXT("OnPossess called: %s"), *GetNameSafe(InPawn));
	UE_LOG(LogTemp, Warning, TEXT("VehiclePawn initialized: %s"), *GetNameSafe(VehiclePawn));
}

In the previous code, when I was testing multiplayer on the client the VehiclePawn is not valid eventhough OnPossess is being called correctly. The only solution I found was to reassign the VehiclePawn on tick.
Could anyone explain to me why is not working and how the should be the proper fix for it? I have the IsLocalController condition as the game has a listeningServer.

The original code is from the VehicalTemplate C++ Unreal Engine 5.5

For anyone having the problem.

The vehicle pawn TObjectPtr was not being replicated, so adding UPROPERTY(Replicated) and to the GetLifeTimeReplicatedProps it works perfectly. Without adding it at tick.

Thanks to chat gpt for the next info that help me to figure it out: (if anyone doesn’t agree with the statement please let me know the reason and the explanation)

“The issue arises because Unreal Engine separates the lifecycle and possession logic between the server and client. When OnPossess is called, it is not guaranteed to be called on the client in the same order or even at all under certain circumstances (e.g., in networked games, the client might not immediately know about the possession due to latency). This results in VehiclePawn potentially being nullptr on the client side even though it was initialized in OnPossess.”