Quick answer: The row name you pass to FindRow does not match the DataTable’s stored FName. Check for case mismatches, extra whitespace, or an FName constructed from a variable that has changed. Also verify the DataTable asset is cooked into the build and the row struct type matches.
Here is how to fix Unreal DataTable FindRow returning nullptr at runtime. You have a DataTable full of item definitions. You call FindRow<FItemRow>(FName("Sword_01"), "") and get nullptr. The row is right there in the editor. You can see it. But at runtime, FindRow cannot find it. This is almost always a naming or cooking issue, and it is made worse by FName’s inconsistent case behavior across platforms.
The Symptom
Calling FindRow on a DataTable returns nullptr even though the row exists when you open the DataTable in the editor. This may happen only in packaged builds, only on certain platforms, or inconsistently between PIE sessions. In Blueprints, the “Get Data Table Row” node shows “Row Not Found” despite the row name being visually correct.
What Causes This
1. FName case mismatch. FNames are case-preserving but their comparison behavior varies. In the editor, FName("sword_01") and FName("Sword_01") often resolve to the same entry due to the FName table. In cooked builds, especially on Linux or dedicated servers, case sensitivity can differ. If your code constructs the FName with different casing than the DataTable row, lookups fail.
2. DataTable not cooked. If the DataTable is only referenced via string path (LoadObject with a path string) and nothing in the cook dependency graph hard-references it, the cooker skips it. The asset exists in the editor but is absent from the packaged build.
3. Struct type mismatch. FindRow<FMyStruct> checks that the DataTable’s row type matches FMyStruct. If you changed the struct or the DataTable was created with a different struct, FindRow returns nullptr with a warning in the log.
4. Row name has invisible characters. If the DataTable was imported from a CSV or JSON file, row names may contain trailing spaces, BOM characters, or other invisible whitespace. These are invisible in the editor but cause FName mismatches.
The Fix
Step 1: Log the actual row names to verify them.
void AMyActor::DebugDataTable()
{
if (!ItemDataTable)
{
UE_LOG(LogTemp, Error, TEXT("DataTable is null!"));
return;
}
TArray<FName> RowNames = ItemDataTable->GetRowNames();
for (const FName& Name : RowNames)
{
UE_LOG(LogTemp, Log, TEXT("Row: '%s' (len=%d)"),
*Name.ToString(), Name.ToString().Len());
}
// Now try the lookup
FName SearchName = FName(TEXT("Sword_01"));
FItemRow* Row = ItemDataTable->FindRow<FItemRow>(
SearchName, TEXT("DebugDataTable"));
UE_LOG(LogTemp, Log, TEXT("FindRow('%s') = %s"),
*SearchName.ToString(), Row ? TEXT("FOUND") : TEXT("NULL"));
}
Step 2: Use exact case from the DataTable. Define row name constants that match the DataTable exactly:
// Define constants for row names to avoid typos and case mismatches
namespace ItemRowNames
{
const FName Sword_01 = FName(TEXT("Sword_01"));
const FName Shield_01 = FName(TEXT("Shield_01"));
const FName Potion_Health = FName(TEXT("Potion_Health"));
}
// Usage:
FItemRow* Row = ItemDataTable->FindRow<FItemRow>(
ItemRowNames::Sword_01, TEXT("GetItem"));
Step 3: Ensure the DataTable is cooked. In Project Settings > Packaging > Additional Asset Directories to Cook, add the directory containing your DataTable. Or add a hard reference from a Blueprint or C++ class that is guaranteed to be cooked:
// Hard reference ensures cooking
UPROPERTY(EditDefaultsOnly, Category = "Data")
UDataTable* ItemDataTable;
// Soft reference needs extra cooking setup
UPROPERTY(EditDefaultsOnly, Category = "Data")
TSoftObjectPtr<UDataTable> ItemDataTableSoft;
Step 4: Validate struct type at load time.
void AMyActor::BeginPlay()
{
Super::BeginPlay();
if (ItemDataTable &&
ItemDataTable->GetRowStruct() != FItemRow::StaticStruct())
{
UE_LOG(LogTemp, Error, TEXT("DataTable struct mismatch! Expected FItemRow, got %s"),
*ItemDataTable->GetRowStruct()->GetName());
}
}
“Log the row names. Always log the row names. The row you think is there and the row that is actually there often differ by one invisible character or one capital letter.”
Why This Works
DataTable rows are keyed by FName, which is Unreal’s interned string type. FNames are stored in a global table and compared by index, not by string content. When you create an FName from a string, the engine looks up (or creates) the table entry. The case of the first FName creation for a given string becomes the canonical case. In the editor, the FName table persists across sessions, so case mismatches may accidentally work. In cooked builds, the table is rebuilt, and the canonical case may differ, causing lookups to fail.
Related Issues
If your DataTable loads but has wrong values after a struct change, reimport it from the source CSV/JSON file. For save game data that references DataTable row names, see Fix: Unreal SaveGame Not Loading After Class Rename for FName persistence issues across version changes.
If DataTable rows work in single-player but fail in multiplayer, check that the DataTable is loaded on both server and client. Replicated FNames must match on both sides.
Log the row names. Compare the strings character by character. Trust the log, not the editor display.