FiveM Housing Scripts Troubleshooting FAQ: 12 Common Issues Fixed
Housing scripts are among the most complex resources on a FiveM server — interiors, furniture, keys, storage, roommates, and property economics all in one system. This guide walks through the 12 most common housing failures on ESX, QBCore, and QBox with diagnostic steps and working code.

Housing scripts are among the most complex resources on a FiveM server. They combine interior management, furniture placement, key distribution, inventory stashes, roommate access, and property economics — any one of which can fail silently and make the whole system look broken.
This guide walks through the twelve housing failures we see most often across ESX, QBCore, and QBox servers. Each section covers how to diagnose the root cause, the fix that resolves it in most cases, and the integration edge case that catches experienced admins.
Quick Diagnosis Table

Before diving into individual issues, match the symptom you see to the most likely root cause.
| Symptom | Most likely cause | First thing to check |
|---|---|---|
| No interaction prompt at door | Zone coords wrong | Print player position, compare to config |
| Prompt appears, teleport fails | Interior coords wrong | Shell resource documentation |
| Furniture resets after restart | Save event not firing | Server-side print in save handler |
| Keys don't work | Identifier format mismatch | SELECT owner FROM housing_properties |
| Stash prompt appears, inventory blank | Stash ID not registered | ox_inventory RegisterStash on start |
| House blip missing | Property data not loaded yet | Callback or delay before blip creation |
| Decorating crashes game | Invalid furniture model | Validate hashes, reduce max items |
| Shell invisible | Resource not started | server.cfg order — shell before housing |
| Purchase silently fails | Money deduction wrong account | Log cash vs bank calls |
| Roommate denied entry | Access table not checked | Trace entry handler |
| Garage door does nothing | Config missing garage coords | Per-property garage_enabled flag |
| Door lock conflicts | Two resources own same door | Remove housing doors from door lock config |
Use this table as a triage filter. Once you've narrowed the bucket, jump to the detailed section below.
1. Can't Enter House
When the entry interaction doesn't work, the issue is either the interaction zone or the teleport destination. Start by confirming where the player actually is:
-- Debug: check if you're in the interaction zone
RegisterCommand('poscheck', function()
local coords = GetEntityCoords(PlayerPedId())
print(('pos: %.2f, %.2f, %.2f heading: %.2f'):format(
coords.x, coords.y, coords.z, GetEntityHeading(PlayerPedId())
))
end, false)
Stand at the door, run /poscheck, and compare to the config entry.
Fix checklist, in order:
- Entry zone coordinates match the actual door location
- Target zone (
ox_target,qb-target) distance is large enough (2.0–3.0 is normal) - Shell interior resource is streaming and
ensure-ed in server.cfg - Player owns the property or has a key (check the DB)
- No other script is blocking interaction — grep for
DisableControlActionat this location
If the entry prompt appears but the teleport fails, the interior coordinates are wrong. Use the shell resource's documented coordinates rather than estimating from inside the editor.
2. Furniture Not Saving
Furniture placement data includes position (x, y, z), rotation (rx, ry, rz), and a model hash for each item. All of it must persist across restarts.
First, confirm what's actually in the database:
SELECT * FROM housing_furniture WHERE property_id = 'apt_1';
-- And the schema
DESCRIBE housing_furniture;
If the table is empty after placing furniture, the save event isn't firing server-side. Drop a print in the save handler and re-test:
RegisterNetEvent('housing:server:saveFurniture', function(propertyId, items)
print(('[housing] save request: property=%s items=%d'):format(propertyId, #items))
-- ...rest of handler
end)
If the event fires but nothing writes, the data format doesn't match the table. Common mismatch: the handler expects individual x, y, z columns, but the table stores position as a JSON string (or vice versa).
3. Key System Broken
Housing keys work like vehicle keys — they map a player identifier to a property ID. When keys don't grant access, the identifier format has drifted:
-- Check property ownership
SELECT owner FROM housing_properties LIMIT 20;
-- What format is your framework currently returning?
-- QBCore citizenid: ABC12345
-- ESX identifier: char1:license:abc...
-- Legacy Steam hex: steam:110000100abcdef
If your server switched identifier types at any point (from Steam hex to license, to citizen IDs), existing property records won't match the new identifiers. Migrate with a single UPDATE:
-- Example: migrate from steam:... to citizenid
UPDATE housing_properties hp
JOIN players p ON p.identifier = hp.owner
SET hp.owner = p.citizenid
WHERE hp.owner LIKE 'steam:%';
Always back up housing_properties before running migrations like this.
4. Stash Not Opening
Housing stashes need a unique identifier that links to your inventory system:
-- Common stash ID format
local stashId = ('house_%s'):format(propertyId) -- e.g., 'house_apt_1'
-- ox_inventory stash registration
exports.ox_inventory:RegisterStash(stashId, 'House Stash', 50, 100000)
If the stash prompt appears but the inventory doesn't open, the stash ID isn't registered with your inventory resource. Three common causes:
- Registration on demand instead of on start —
ox_inventoryrequires stashes to be registered on server start, not when the player tries to open them. - ID format mismatch — the housing script generates
house_apt_1, the inventory expectsapt_1_stash. - Slots/weight defaulted to 0 — the stash exists but has 0 slots, so opening it succeeds but shows empty.
5. House Blip Not Showing
Blips for owned properties are created client-side after the player's property data loads. If the blip doesn't appear:
-- This should run *after* the player's properties are fetched
local function createPropertyBlip(property)
local blip = AddBlipForCoord(property.x, property.y, property.z)
SetBlipSprite(blip, 40) -- House icon
SetBlipDisplay(blip, 4)
SetBlipScale(blip, 0.7)
SetBlipColour(blip, 2)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(property.label)
EndTextCommandSetBlipName(blip)
end
Two common causes for missing blips:
- Data race — the blip code runs before the property list arrives from the server. Use a callback or event-based approach.
- Empty ownership query — the server query returned no rows, often because of the identifier mismatch from issue #3.
6. Decorating Mode Crashes
Furniture placement mode renders a preview prop that follows the player's cursor. Crashes in this mode come from:
- Invalid furniture model hashes — the model doesn't exist in GTA V
- Too many props spawned simultaneously during placement
- Memory overflow from high-poly furniture models
Reduce the maximum placeable items per property (20–30 is safe on mid-range clients) and validate all furniture hashes:
-- Pre-validate furniture list on resource start
for _, item in ipairs(Config.Furniture) do
if not IsModelValid(GetHashKey(item.model)) then
print(('[housing] INVALID furniture model: %s'):format(item.model))
end
end
This catches bad entries at startup instead of crashing clients at decoration time.
7. Shell Interior Not Loading
Shell interiors are standalone streaming resources with their own stream/ folder. Verify the shell is actually loaded:
local interior = GetInteriorAtCoords(shellX, shellY, shellZ)
print(('Interior ID: %d'):format(interior)) -- Non-zero means loaded
if interior ~= 0 then
PinInteriorInMemory(interior)
LoadInterior(interior)
end
Fix checklist:
- Shell resource is
ensure-ed in server.cfg - Shell is listed before the housing resource in server.cfg
- Shell model files exist in its
stream/folder - Interior coordinates match the shell's actual world position
- Some shells occupy the same space as default GTA interiors — call
RemoveIplto clear conflicts
8. House Purchase Failing
Purchase transactions involve multiple steps: checking funds, marking the property as sold, deducting money, and assigning the key. If any single step fails silently, the player ends up in a half-completed state.
Instrument the server-side handler:
RegisterNetEvent('housing:server:buyProperty', function(propertyId)
local source = source
local xPlayer = GetPlayerFromId(source)
print(('[housing] purchase: src=%d prop=%s'):format(source, propertyId))
if not Config.Properties[propertyId] then
print('[housing] unknown property')
return
end
if Config.Properties[propertyId].owner then
print('[housing] already owned')
return
end
local price = Config.Properties[propertyId].price
if not xPlayer.removeAccountMoney('bank', price) then
print(('[housing] insufficient funds: need %d'):format(price))
return
end
-- success path
end)
Most silent failures are one of: property price set to 0 (math error), the money deduction targeting the wrong account type (cash vs bank), or the property already flagged as sold from a previous half-completed purchase.
9. Roommate Access Not Working
Roommates are stored as additional entries in a housing_access table, separate from the owner field in housing_properties:
SELECT * FROM housing_access WHERE property_id = 'apt_1';
If the roommate was added but can't enter, the entry handler is probably only checking the owner field. Audit the entry logic:
local function canEnter(identifier, propertyId)
if Config.Properties[propertyId].owner == identifier then
return true
end
local roommates = MySQL.query.await(
'SELECT identifier FROM housing_access WHERE property_id = ?',
{ propertyId }
)
for _, r in ipairs(roommates) do
if r.identifier == identifier then return true end
end
return false
end
The handler must check both sources before denying access.
10. Housing Garage Not Functioning
Housing garages are a secondary feature that requires explicit coordinates in the property config:
Config.Properties['apt_1'] = {
label = 'Apartment 1',
entry = vector3(100.0, 200.0, 30.0),
interior = 'shell_1',
garage_enabled = true,
garage_coords = vector3(105.0, 205.0, 30.0),
garage_heading = 90.0,
}
If the garage zone doesn't appear, check that garage_enabled is true for that property and the coordinates sit in a drivable area (not inside a wall or clipping through the property's shell).
11. Door Lock Conflicts
If a door lock resource (qb-doorlock, ox_doorlock) and the housing script both try to control the same door entity, they'll conflict. Symptoms: door state flickers, interaction doesn't fire, or the door locks itself immediately after opening.
The fix is straightforward — remove the door entries from your door lock config for any door managed by housing. Let the housing script handle its own door states. You can't have two resources owning the same door entity.
12. Property Tax System Not Working
Tax collection runs on a server-side timer, typically every hour or day:
CreateThread(function()
while true do
Wait(3600000) -- hourly
local properties = MySQL.query.await(
'SELECT id, owner, tax FROM housing_properties WHERE owner IS NOT NULL'
)
for _, p in ipairs(properties) do
local ok = chargeTax(p.owner, p.tax)
print(('[housing-tax] %s owner=%s tax=%d ok=%s'):format(
p.id, p.owner, p.tax, tostring(ok)
))
end
end
end)
If taxes aren't collecting, one of:
- Timer isn't running — check for server-side errors at startup
- Bank deduction failing silently — log the return value of each deduction
- Tax calculation returns 0 — a math or config error
- Thread crashes on error — wrap the inner block in a pcall so a single failure doesn't kill the timer
Recommended Scripts
When you need housing scripts that just work — framework-native, performance-tested, documented — check the VertexMods catalog:
- FiveM Housing Scripts — Premium and free housing resources
- Best FiveM Scripts 2026 — Top-rated scripts across all categories
- FiveM Troubleshooting Guide — Broader troubleshooting playbook
- FiveM Error Codes Fixes — Decoding common error messages
Frequently Asked Questions
Why can't I enter my house in FiveM?
Entry failures have three common causes: the interaction zone is misconfigured (coordinates don't match the door), the shell interior hasn't streamed in yet, or the player isn't recognized as the owner. Print the player's position when standing at the door and compare to the config. If coordinates match but teleport fails, the interior coordinates are wrong — use the shell resource's documented coordinates, don't estimate.
Why isn't furniture saving in my FiveM housing script?
Furniture persistence fails when the save event doesn't trigger, or when the data format doesn't match the database schema. Add a server-side print in the save handler to confirm it fires, then inspect the housing_furniture table. If the event fires but nothing writes, position is usually stored wrong — check whether your script expects individual float columns (x, y, z) or a JSON blob.
Why is the housing key system broken?
Housing keys map a player identifier to a property ID. If keys don't grant access, the identifier format doesn't match. Most commonly, the server switched from Steam hex to license IDs or citizen IDs at some point, and existing property records were never migrated. Run SELECT owner FROM housing_properties and compare to what your framework returns now.
Why isn't the house stash opening?
Stash failures occur when the inventory system can't find the stash identifier. Each house needs a unique stash ID (commonly 'house_<propertyId>') registered with your inventory resource. ox_inventory requires registration via exports.ox_inventory:RegisterStash on server start, not on demand. Verify the stash ID format matches what the housing script generates.
Why isn't the house blip showing on the map?
Blips for owned properties are created client-side after the player's property data loads. If the blip doesn't appear, either the ownership check returned empty results, or the blip code ran before the data arrived. Add a small wait or use a callback-based approach so blip creation fires after the player's owned-properties list is fully populated.
Why does decorating mode crash my game?
Decoration crashes come from three root causes: invalid furniture model hashes (the model doesn't exist in GTA V), too many props spawned simultaneously during placement, or memory overflow from high-poly models. Reduce the max placeable items per property, validate all furniture hashes against the GTA V prop list, and clear the asset cache between crashes.
Why isn't the shell interior loading for my house?
Shell interiors are separate streaming resources. If the shell doesn't render, start the shell resource *before* the housing resource in server.cfg, verify the shell's model files exist in its stream/ folder, and confirm the interior coordinates match the shell's actual position. Some shells require IPL removal (RemoveIpl) if they occupy the same space as a default GTA interior.

