How to Fix Pre Populated SQLite Database Error in a Unity Android Application

If you’re trying to use a pre populated SQLite database in your Unity Android game, you’ve probably run into the infamous:

SqliteSyntaxException: file is encrypted or is not a database

Well, I certainly did, I thought it would be simple it worked flawlessly on Windows. But as soon as I built my project for Android, everything broke. This is how I figured out the problem, fixed it, and improved it with some extra functionality for practice.

The Original Code and What Went Wrong

At first, I used this basic code to copy the SQLite DB from StreamingAssets to a writable location and then open it:

public void OpenDB(string p) // p is the database name
{
string filepath = Application.persistentDataPath + "/" + p;

if (!File.Exists(filepath))
{
WWW loadDB = new WWW("jar:file://" + Application.dataPath + "!/assets/" + p);
while (!loadDB.isDone) { } // BAD PRACTICE

File.WriteAllBytes(filepath, loadDB.bytes);
}

string connection = "URI=file:" + filepath;
dbcon = new SqliteConnection(connection);
dbcon.Open();
}

The Error

Running this on Android gave me this error:

SqliteSyntaxException: file is encrypted or is not a database

This usually means:

  • The file is not a valid SQLite database
  • It got corrupted during the copy
  • The DB is being read before it’s fully written

The Problem Line

File.WriteAllBytes(filepath, loadDB.bytes);

I was trying to write data before it was safely downloaded. And to make matters worse, WWW is outdated and shouldn’t be used anymore. Also, blocking the main thread with while(!loadDB.isDone) is dangerous in Unity.

The Safe and Correct Way UnityWebRequest + Async Loading

Here’s how I fixed it using UnityWebRequest and a coroutine:

using UnityEngine;
using System.IO;
using Mono.Data.Sqlite;
using UnityEngine.Networking;
using System.Collections;

public class DatabaseLoader : MonoBehaviour
{
private SqliteConnection dbcon;

public void Start()
{
StartCoroutine(LoadAndOpenDB("myDatabase.db")); // Replace with your DB file name
}

private IEnumerator LoadAndOpenDB(string dbName)
{
string dbPath = Path.Combine(Application.persistentDataPath, dbName);

if (!File.Exists(dbPath))
{
string sourcePath = Path.Combine(Application.streamingAssetsPath, dbName);

#if UNITY_ANDROID
UnityWebRequest www = UnityWebRequest.Get(sourcePath);
yield return www.SendWebRequest();

if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Error loading DB: " + www.error);
yield break;
}

File.WriteAllBytes(dbPath, www.downloadHandler.data);
#else
File.Copy(sourcePath, dbPath);
#endif
Debug.Log("Database copied to: " + dbPath);
}

string connection = "URI=file:" + dbPath;
dbcon = new SqliteConnection(connection);
dbcon.Open();
Debug.Log("Database opened successfully.");
}
}

This version is platform-safe, avoids thread-blocking, and properly waits for the database to download before writing it.

Extra Functionalities I Added for Practice

Once the DB was working, I wanted to explore further. So, I added some practical utilities:

Check if the Database is Valid

public bool IsValidDatabase(string dbPath)
{
try
{
string connection = "URI=file:" + dbPath;
using (var conn = new SqliteConnection(connection))
{
conn.Open();
using (var cmd = new SqliteCommand("SELECT name FROM sqlite_master WHERE type='table';", conn))
{
var reader = cmd.ExecuteReader();
return reader.HasRows;
}
}
}
catch
{
return false;
}
}

List All Table in the Database

public void ListAllTables()
{
using (var cmd = dbcon.CreateCommand())
{
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table';";
var reader = cmd.ExecuteReader();
while (reader.Read())
{
Debug.Log("Table: " + reader.GetString(0));
}
}
}

Query Any Table

public void QueryTable(string tableName)
{
using (var cmd = dbcon.CreateCommand())
{
cmd.CommandText = $"SELECT * FROM {tableName} LIMIT 10;";
var reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
while (reader.Read())
{
string row = "";
for (int i = 0; i < fieldCount; i++)
row += reader.GetName(i) + ": " + reader.GetValue(i) + " ";
Debug.Log(row);
}
}
}

Debug Tips I Learned the Hard Way

  • Log the file size after WriteAllBytes() sometimes it’s 0 KB, which means it failed silently.
  • Compare hashes (e.g., MD5) between original and copied DB to verify file integrity.
  • Use tools like DB Browser for SQLite to open the pre-populated DB and make sure it’s actually a valid file with tables and data.

Final Thought

Using SQLite with Unity on Android seems simple at first, but it’s riddled with hidden pitfalls. If you’re working with a pre-populated database, avoid blocking the main thread, stop using the outdated WWW class, and always verify the file after writing. Instead, use UnityWebRequest to safely read from StreamingAssets on Android. Once I made these adjustments, everything fell into place, and I could finally focus on building my game not fixing mysterious database errors.

Related blog posts