Quick answer
StructureSpawn.recycleCreep(creep) irreversibly ends the target Creep and drops part of the resources used for spawning and Boosting, based on remaining lifetime. The target must be adjacent to your Spawn. Because the operation is destructive, use a one-time request with an exact Spawn name, exact Creep name, enabled: true, and confirmed: true. Disable the request before submitting, restore it only if the call fails, and verify the next tick.
recycleCreep() versus suicide()
| Method | Caller and range | Resource behavior |
|---|---|---|
spawn.recycleCreep(creep) | Your Spawn; target must be adjacent. | Drops a lifetime-based portion of spawning and Boost resources. |
creep.suicide() | The Creep itself; no Spawn adjacency requirement. | Not the Spawn recycling workflow described here. |
Use recycling when the Creep can safely return to your Spawn and recovering part of the investment is useful. Evaluate suicide() separately when immediate removal is genuinely required. This guide never calls it automatically after a recycling failure.
Resource-return boundaries
The official API says recycling drops up to 100% of the resources spent on spawning and Boosting, depending on the Creep's remaining lifetime. Energy return is also limited to 125 units per body part.
- Lower TTL generally means a smaller returned portion.
- Expensive parts remain subject to the per-part Energy cap.
- Boost compounds may be included in the server-calculated drops.
- The original body cost is not the same as the guaranteed refund.
- The exact result should be observed after the action, not reimplemented from guesses.
This article does not claim a specific recovered amount without a live result.
Create a one-time confirmation request
Enter a deliberate request in the Screeps Console:
Memory.recycleRequests ??= {};
Memory.recycleRequests.OldWorker1 = {
enabled: true,
confirmed: true,
spawnName: 'Spawn1',
creepName: 'OldWorker1',
reason: 'role-replaced',
requestedAt: Game.time
};
enabled and confirmed are player-defined safety fields, not official API arguments. They make the destructive intent explicit and give the loop a single request to close after submission.
Evaluate the request before acting
function evaluateRecycleRequest(input) {
const {
requestExists,
enabled,
confirmed,
spawnExists,
creepExists,
creepSpawning,
spawnOwned,
creepOwned,
spawnActive,
isNearSpawn
} = input;
if (!requestExists || enabled !== true) {
return {
ready: false,
action: 'wait',
reason: 'request-disabled'
};
}
if (confirmed !== true) {
return {
ready: false,
action: 'wait',
reason: 'confirmation-required'
};
}
if (!spawnExists) {
return {
ready: false,
action: 'wait',
reason: 'spawn-missing'
};
}
if (!creepExists) {
return {
ready: false,
action: 'close',
reason: 'creep-missing'
};
}
if (creepSpawning) {
return {
ready: false,
action: 'wait',
reason: 'creep-spawning'
};
}
if (!spawnOwned || !creepOwned) {
return {
ready: false,
action: 'close',
reason: 'ownership-invalid'
};
}
if (!spawnActive) {
return {
ready: false,
action: 'wait',
reason: 'spawn-inactive'
};
}
if (!isNearSpawn) {
return {
ready: false,
action: 'move',
reason: 'move-to-spawn'
};
}
return {
ready: true,
action: 'recycle',
reason: 'ready'
};
}
This pure function distinguishes waiting, moving, closing an invalid request, and executing the irreversible action.
Complete recycling mission
State impact: this script reads and updates one entry in Memory.recycleRequests, may move the named Creep, and may submit one irreversible recycling action. It disables the request before the call and re-enables it only when the API returns a failure code.
function evaluateRecycleRequest(input) {
const {
requestExists,
enabled,
confirmed,
spawnExists,
creepExists,
creepSpawning,
spawnOwned,
creepOwned,
spawnActive,
isNearSpawn
} = input;
if (!requestExists || enabled !== true) {
return {
ready: false,
action: 'wait',
reason: 'request-disabled'
};
}
if (confirmed !== true) {
return {
ready: false,
action: 'wait',
reason: 'confirmation-required'
};
}
if (!spawnExists) {
return {
ready: false,
action: 'wait',
reason: 'spawn-missing'
};
}
if (!creepExists) {
return {
ready: false,
action: 'close',
reason: 'creep-missing'
};
}
if (creepSpawning) {
return {
ready: false,
action: 'wait',
reason: 'creep-spawning'
};
}
if (!spawnOwned || !creepOwned) {
return {
ready: false,
action: 'close',
reason: 'ownership-invalid'
};
}
if (!spawnActive) {
return {
ready: false,
action: 'wait',
reason: 'spawn-inactive'
};
}
if (!isNearSpawn) {
return {
ready: false,
action: 'move',
reason: 'move-to-spawn'
};
}
return {
ready: true,
action: 'recycle',
reason: 'ready'
};
}
function runRecycleRequest(requestKey) {
const request = Memory.recycleRequests?.[requestKey];
if (!request || request.enabled !== true) {
return {
status: 'request-disabled'
};
}
const spawn = typeof request.spawnName === 'string'
? Game.spawns[request.spawnName]
: null;
const creep = typeof request.creepName === 'string'
? Game.creeps[request.creepName]
: null;
const decision = evaluateRecycleRequest({
requestExists: true,
enabled: request.enabled,
confirmed: request.confirmed,
spawnExists: Boolean(spawn),
creepExists: Boolean(creep),
creepSpawning: creep?.spawning === true,
spawnOwned: spawn?.my === true,
creepOwned: creep?.my === true,
spawnActive: Boolean(spawn?.isActive()),
isNearSpawn: Boolean(
spawn
&& creep
&& creep.pos.isNearTo(spawn)
)
});
request.lastStatus = decision.reason;
request.lastCheckedAt = Game.time;
if (decision.action === 'close') {
request.enabled = false;
request.closedAt = Game.time;
return {
status: decision.reason
};
}
if (
decision.action === 'move'
&& creep
&& spawn
) {
const moveResult = creep.moveTo(spawn, {
range: 1,
reusePath: 10
});
request.lastMoveResult = moveResult;
request.lastMoveAt = Game.time;
return {
status: 'moving-to-spawn',
moveResult
};
}
if (!decision.ready || !spawn || !creep) {
return {
status: decision.reason
};
}
request.enabled = false;
request.submittedAt = Game.time;
const result = spawn.recycleCreep(creep);
request.lastResult = result;
request.lastResultAt = Game.time;
if (result !== OK) {
request.enabled = true;
console.log(JSON.stringify({
type: 'recycle-creep-failed',
spawnName: spawn.name,
creepName: creep.name,
result
}));
}
return {
status: result === OK
? 'recycle-submitted'
: 'recycle-failed',
result
};
}
module.exports.loop = function () {
runRecycleRequest('OldWorker1');
};
Disabling before the call prevents a later exception elsewhere in the loop from causing repeated submissions without a fresh object check. A failed API result reopens the request for the next controlled retry.
Why this guide does not reject a busy Spawn
The current official recycleCreep() description requires adjacency but does not state that the Spawn must be idle. Its documented return-code table also does not include ERR_BUSY. Therefore this guide does not copy the renewCreep() busy condition into recycling.
Always trust and log the real return value. Do not invent a shared precondition merely because both methods belong to StructureSpawn.
Return-code troubleshooting
| Code | Likely cause | Action |
|---|---|---|
OK | The recycling operation was scheduled. | Verify disappearance and resource drops on the next tick. |
ERR_NOT_OWNER | The Spawn or Creep is not yours. | Close the unsafe request and inspect object selection. |
ERR_INVALID_TARGET | The selected target is not a Creep. | Validate the exact name and object type. |
ERR_NOT_IN_RANGE | The Creep is not adjacent. | Move to range 1 and retry later. |
ERR_RCL_NOT_ENOUGH | The Spawn is inactive at the current RCL. | Check the Controller and isActive(). |
ERR_BUSY is not in the current documented return set for recycling.
Verify disappearance and drops later
OK schedules the operation. It does not require Game.creeps[name] to disappear during the same script execution. On the next tick, verify:
const request = Memory.recycleRequests?.OldWorker1;
const creepStillExists = Boolean(
Game.creeps.OldWorker1
);
console.log(JSON.stringify({
tick: Game.time,
requestEnabled: request?.enabled ?? null,
lastResult: request?.lastResult ?? null,
creepStillExists
}));
Inspect the adjacent dropped resources in the room when a live test is available. This article does not fabricate the returned quantities.
When not to recycle automatically
- The Creep is the only harvester, hauler, or defender.
- No replacement has completed the handoff.
- The Creep carries resources or task state that must be delivered first.
- The return route is longer than the remaining lifetime.
- The name or intended target is ambiguous.
- The trigger is only low TTL rather than a completed retirement decision.
Recycling should be triggered by explicit task state or human confirmation, not merely by age or temporary role surplus.
Clean related Memory after success
After the next tick confirms that the Creep is gone, clean the old Creep entry and only the custom indexes your project explicitly owns. Review the dead-Creep Memory cleanup guide. Do not recursively delete every matching name from unrelated Memory modules.
Debugging checklist
- Require an exact Spawn name and exact Creep name.
- Require both
enabled: trueandconfirmed: true. - Validate object existence, ownership, Spawn activity, and Creep spawning state.
- Move to an adjacent square and save the movement result.
- Do not require
spawn.spawningto be empty unless live evidence or future docs add that rule. - Disable the request before the irreversible call.
- Re-enable only when the real result is not
OK. - Never auto-fallback to
suicide(). - Verify disappearance and drops on the next tick.
- Clean only explicitly managed Memory indexes afterward.
- Do not equate original body cost with guaranteed returned resources.
Scope and next steps
This guide handles one explicitly named regular Creep. It does not detect role surplus, schedule a multi-Creep recycling queue, solve Spawn traffic, select a nearest Spawn, route across rooms, recycle Power Creeps, estimate exact drops, or automate suicide(). Live recycling and resource-drop verification remain Pending.
Frequently asked questions
Does recycling return the full body cost?
Not necessarily. Remaining lifetime and the 125-Energy-per-part cap affect the server-calculated drop.
Must the Spawn be idle?
The current documented recycling method does not require that condition or list ERR_BUSY.
Should failure call suicide() automatically?
No. A recoverable range, target, ownership, or RCL problem should not escalate automatically.
Does OK remove the Creep immediately from Game.creeps?
Treat OK as scheduled and verify the next tick.