Quick answer: Unity serializes fields by name. When you rename a field, the serialized data still uses the old name, so Unity cannot find a match and assigns the default value. Use the [FormerlySerializedAs] attribute with the old name to tell Unity to look for both names during deserialization.
Here is how to fix Unity serialization field lost after rename. You rename a variable in your MonoBehaviour or ScriptableObject, go back to Unity, and every instance of that component has lost its value. The Inspector shows the default. Hours of hand-tuned configuration on dozens of prefabs, gone. This happens because Unity serializes fields by name, and a rename breaks the link between the stored data and the code that reads it.
How Unity Serialization Works
Unity's serialization system writes the name and value of each serialized field into the asset file (scene, prefab, or ScriptableObject). When the asset is loaded, Unity matches stored names to current field names in the script. If the names match, the stored value is applied. If the name is not found in the script, the stored value is ignored and the field gets its default.
This is a flat name-based lookup. There is no concept of field identity, history, or refactoring awareness. As far as Unity is concerned, renaming speed to moveSpeed is the same as deleting speed and creating a brand new moveSpeed field.
This affects every serialized field: [SerializeField] private fields, public fields, and fields in [Serializable] nested classes. It applies to MonoBehaviours, ScriptableObjects, and any custom class that Unity serializes.
The FormerlySerializedAs Attribute
Unity provides [FormerlySerializedAs] specifically for this situation. It tells the deserializer to check for an alternative name when the current name is not found in the serialized data.
using UnityEngine;
using UnityEngine.Serialization;
public class PlayerMovement : MonoBehaviour
{
// Old field name was "speed"
[FormerlySerializedAs("speed")]
[SerializeField] private float _moveSpeed = 5f;
// Old field name was "jumpHeight"
[FormerlySerializedAs("jumpHeight")]
[SerializeField] private float _jumpForce = 10f;
// Multiple renames: field was "hp", then "health", now "_maxHealth"
[FormerlySerializedAs("hp")]
[FormerlySerializedAs("health")]
[SerializeField] private int _maxHealth = 100;
}
The attribute takes the old field name as a string. When Unity deserializes an asset and cannot find a field named _moveSpeed, it checks the FormerlySerializedAs attributes and looks for speed instead. If found, it uses that value. You can stack multiple attributes to handle a chain of renames over time.
The critical detail is that you must add this attribute before or at the same time as the rename. If you rename first, save, and then add the attribute, it is too late. Unity already failed to find the old name, assigned defaults, and saved the defaults over the old data.
Recovering Data After a Rename Without the Attribute
If you already renamed without FormerlySerializedAs and the data is gone from the Inspector, check version control first. If the asset files (.scene, .prefab, .asset) have not been committed with the new defaults, revert them. Then add the attribute and let Unity re-import.
If the assets were already saved with defaults, you can try to recover from the serialized file directly. Unity stores scenes and prefabs as YAML (if using text serialization). Open the file in a text editor and search for the old field name.
--- !u!114 &123456789
MonoBehaviour:
m_Script: {fileID: 11500000, guid: abc123...}
speed: 7.5
jumpHeight: 12
If the old name still appears in the YAML with the correct value, the data has not been overwritten yet. Add [FormerlySerializedAs] to your script and Unity will pick it up on the next import. If the file shows the new name with default values, the old data is gone and must be restored from version control or manually re-entered.
Prefab Serialization Complications
Prefab instances and variants add another layer of complexity. A prefab stores a base set of values. Instances and variants store only the overrides — fields that differ from the base. When you rename a field, three things can go wrong.
1. Base prefab loses values. The base prefab's serialized data uses the old name. Without FormerlySerializedAs, the base reverts to defaults.
2. Instance overrides are lost. Overrides reference fields by name. A renamed field means the override key no longer matches, and the instance falls back to the base value (which is also wrong if the base was not migrated).
3. Variant chains break. Prefab variants inherit from other prefabs. Each level in the chain stores its own overrides. A rename breaks overrides at every level independently.
// Force-resave all prefabs to migrate serialized data
// Run this from an Editor script after adding FormerlySerializedAs
using UnityEditor;
using UnityEngine;
public static class PrefabMigrator
{
[MenuItem("Tools/Resave All Prefabs")]
public static void ResaveAllPrefabs()
{
var guids = AssetDatabase.FindAssets("t:Prefab");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (prefab.GetComponentInChildren<PlayerMovement>() != null)
{
EditorUtility.SetDirty(prefab);
Debug.Log($"Marked dirty: {path}");
}
}
AssetDatabase.SaveAssets();
Debug.Log("All affected prefabs resaved");
}
}
Run this Editor script after adding the FormerlySerializedAs attribute. It loads every prefab, marks the ones containing your component as dirty, and saves them. This forces Unity to read the old name via the attribute, write the value under the new name, and persist it.
ScriptableObject Data Migration
ScriptableObjects are stored as .asset files. The same name-based serialization rules apply. If you have configuration data, item databases, or dialogue trees stored in ScriptableObjects, a field rename without migration loses all that data.
[CreateAssetMenu(fileName = "NewWeapon", menuName = "Game/Weapon")]
public class WeaponData : ScriptableObject
{
[FormerlySerializedAs("damage")]
[SerializeField] private float _baseDamage;
[FormerlySerializedAs("fireRate")]
[SerializeField] private float _attackSpeed;
[FormerlySerializedAs("range")]
[SerializeField] private float _effectiveRange;
}
The approach is the same: add the attribute, reopen and resave every .asset file that uses the ScriptableObject type. For large projects with hundreds of assets, write an Editor script to batch this operation.
Make it a team convention to always add
[FormerlySerializedAs]when renaming any serialized field. The attribute costs nothing at runtime and prevents data loss. Treat it like a database migration — you would never rename a database column without a migration script.
Prevention Best Practices
The safest workflow is to always use the [SerializeField] attribute with private fields and a naming convention like underscore prefix. This separates the code-facing name (which you refactor freely) from any public API. When you do rename, the steps are: add [FormerlySerializedAs("oldName")], rename the field, save, open each scene and prefab, verify values, and commit.
Consider adding a pre-commit hook or CI check that detects removed serialized field names and warns if no corresponding FormerlySerializedAs was added. This catches mistakes before they destroy data across the team.
Related Issues
If the field shows in the Inspector but resets after entering Play mode, see SerializedField not showing in Inspector. If renamed fields in Addressable prefabs fail to load, check Addressables failed to load. For fields lost specifically in multiplayer NetworkBehaviour scripts, see NetworkObject not spawning.
Add FormerlySerializedAs before you rename, not after. After is too late if Unity has already saved defaults over your data.