How to Remove a Construction Site Safely in Screeps
Use ConstructionSite.remove() in Screeps: resolve the site by ID, handle OK or ERR_NOT_OWNER, and verify the original site is gone on a later visible tick.
//9 min read
ScreepsConstructionSiteJavaScriptDebugging
Quick answer: ConstructionSite.remove()
API docs answer: call site.remove() on the exact visible ConstructionSite. The method documents two return values: OK when removal is scheduled and ERR_NOT_OWNER when the site is neither yours nor in your room.
If you only have the site ID, resolve it with Game.getObjectById(id) first. If construction has already completed, the target is a Structure and uses Structure.destroy() instead.
const result = site.remove();
After OK, keep the original site ID and position, then verify the original site is absent on a later tick when the room is visible.
First confirm that the target is still a Construction Site
A finished building is a Structure, not a ConstructionSite. The removal APIs are different:
Target now
Use
Do not substitute
Unfinished Construction Site
site.remove()
structure.destroy()
Completed Structure
structure.destroy()
site.remove()
If a Builder could have completed the site since you last inspected it, retrieve the current object again before mutating anything.
Inspect the exact site before removing it
If you already know the site ID, retrieve it with Game.getObjectById() and review its fields. If you only know the tile, this read-only probe finds Construction Sites at that position. Replace the room name and example coordinates before using it:
const roomName = 'REPLACE_WITH_ROOM_NAME';
const x = 20; // Example only: replace.
const y = 20; // Example only: replace.
const room = Game.rooms[roomName];
if (!room) {
console.log('Room is not visible.');
} else {
const sites = room.lookForAt(
LOOK_CONSTRUCTION_SITES,
x,
y
);
console.log(JSON.stringify(
sites.map(function (site) {
return {
id: site.id,
my: site.my,
roomName: site.pos.roomName,
roomControlledByMe:
site.room.controller?.my === true,
x: site.pos.x,
y: site.pos.y,
structureType: site.structureType,
progress: site.progress,
progressTotal: site.progressTotal
};
})
));
}
Confirm the ID, room, coordinates, structureType, and progress before continuing. The my field is useful context, but site.my === false does not by itself mean remove() must fail. The official return-code condition is that the site is not yours and is not in your room. Current public engine 4.3.2 implements that boundary by accepting either site ownership or room-controller ownership.
Remove the confirmed site once
After the read-only inspection matches the target you meant to remove, retrieve the same ID again and submit one request:
const site = Game.getObjectById('REPLACE_WITH_SITE_ID');
if (!(site instanceof ConstructionSite)) {
console.log('Target is not a visible ConstructionSite.');
} else {
const snapshot = {
siteId: site.id,
roomName: site.pos.roomName,
x: site.pos.x,
y: site.pos.y,
structureType: site.structureType,
progress: site.progress,
progressTotal: site.progressTotal
};
const result = site.remove();
console.log(JSON.stringify({
...snapshot,
result
}));
}
Do not turn an unexpected result into an automatic retry. If the call does not return OK, inspect the target again instead of assuming the same ID and state are still correct.
ConstructionSite.remove() return codes
Return code
What it means
What to do next
OK
The removal operation was scheduled successfully.
Keep the original ID and position; verify on a later tick.
ERR_NOT_OWNER
The site is not yours and is not in your room.
Recheck the exact object and room. Do not use site.my alone as the eligibility test.
Those are the two documented return values for ConstructionSite.remove(). For a broader return-code lookup, use the Screeps error-code reference.
Verify the original site on a later visible tick
OK is a request result, not the later observation. On a later tick, confirm room vision first. Then check the original ID and inspect the original tile so a replacement site is not mistaken for the removed object.
Game.getObjectById() can only access objects in rooms currently visible to you. That is why a bare null is not enough when the room is out of vision. Once the room is visible, an absent original ID verifies that the original site is gone; another site at the same coordinates is a different object and should be reported separately.
If automation can trigger removal, make the request one-shot
The large Memory workflow is optional. It is useful only when removal is initiated by automation rather than by one deliberate Console action. The confirmation string and expected position below are linqingan.com safety policy, not Screeps API requirements.
The request is consumed before the mutation call, so a later tick cannot silently resubmit the same removal. Notice that this guard validates target identity but does not reject site.my === false; remove() itself returns the documented ownership result.
Failure and evidence boundaries
ID resolves to null: the object may be gone, the ID may be wrong, or the room may not be visible. Check room vision before recording success.
The site completed: stop this workflow. A completed Structure uses destroy(), which has its own return-code boundaries.
Existing progress: capture progress and progressTotal before removal if the invested work matters to your decision. This guide does not claim that removed progress is transferred or refunded.
Another site appears on the tile: compare IDs. Coordinate occupancy alone does not prove the original site survived.
Bulk cleanup: this guide intentionally covers one exact target. Batch deletion needs separate target-selection and confirmation policy.
No live Console or official-shard removal trace is claimed here. The API behavior, visibility rule, current engine ownership boundary, and code paths were checked against current official sources and offline logic; live multi-tick verification remains pending.
ConstructionSite.remove() FAQ
What does ConstructionSite.remove() return in Screeps?
The documented return values are OK when removal is scheduled and ERR_NOT_OWNER when the site is neither yours nor in your room. A missing object from Game.getObjectById() is a lookup state to handle before calling remove(), not another documented return value from the method.
Can I remove a construction site when I only know its ID?
Yes. Resolve the ID with Game.getObjectById(id), confirm that the current object is the intended ConstructionSite, then call remove(). If the room is not visible, do not treat a missing lookup as proof that removal succeeded.