Quick answer
StructureSpawn.renewCreep(creep) increases a regular Creep's remaining lifetime when the Creep is adjacent to the Spawn. The Spawn must not be spawning another Creep, the target cannot contain a CLAIM part, the Spawn must have enough of its own Energy, and every Boost on the target will be removed. Do not renew from a TTL threshold alone: calculate the step, confirm Boost loss, coordinate Spawn priority, and stop at an explicit target TTL.
Official TTL and Energy formulas
One successful renewal call uses the official formulas:
added TTL = floor(600 / body size)
Energy cost = ceil(creep cost / 2.5 / body size)
The body size is the total number of body parts, including damaged parts. The Creep cost is the original sum of the official BODYPART_COST values for every part type.
function getBodyCost(body) {
return body.reduce((total, part) => {
const cost = BODYPART_COST[part.type];
if (!Number.isFinite(cost)) {
throw new TypeError(
'Unknown body part: ' + String(part.type)
);
}
return total + cost;
}, 0);
}
function getRenewStep(body) {
if (!Array.isArray(body) || body.length === 0) {
return {
valid: false,
reason: 'body-invalid'
};
}
const bodyCost = getBodyCost(body);
return {
valid: true,
reason: 'ready',
bodySize: body.length,
bodyCost,
addedTicks: Math.floor(600 / body.length),
energyCost: Math.ceil(
bodyCost / 2.5 / body.length
)
};
}
A larger body gains fewer ticks per call. Expensive body parts also increase the Energy cost. These formulas describe one execution, not whether renewing is strategically efficient.
Renewal removes every Boost
The official API explicitly states that renewing removes all Boosts from the target. Inspect the body before allowing the action:
function getBoostedParts(creep) {
return creep.body.filter(part =>
typeof part.boost === 'string'
);
}
This guide uses a player-defined safety flag:
allowBoostRemoval === true
The flag is not an official argument. It prevents an automatic TTL rule from silently stripping a boosted fighter, miner, or upgrader. When Boosts must remain, create a replacement instead.
Creeps with CLAIM cannot be renewed
function hasClaimPart(creep) {
return creep.body.some(part =>
part.type === CLAIM
);
}
Check the complete body, not only active parts. The API rejects any target with a CLAIM part. Claimer lifetime should be managed through replacement production rather than renewal.
Choose a threshold and target TTL
A renewal threshold determines when the Creep starts returning to the Spawn. A target TTL determines when renewal stops:
const renewThreshold = 300;
const targetTtl = 1200;
These numbers are examples, not official recommendations. A useful threshold depends on:
- travel time back to the Spawn;
- body spawn time and the replacement lead time;
- current Spawn queue pressure;
- whether the Creep can leave its work position;
- how long it should remain after renewal;
- whether another Creep is already taking over.
A threshold that is too high wastes work and Spawn time. One that is too low may let the Creep die before it arrives. A target TTL avoids repeatedly calling until ERR_FULL.
Run read-only checks first
const spawn = Game.spawns.Spawn1;
const creep = Game.creeps.Worker1;
console.log(JSON.stringify({
spawnFound: Boolean(spawn),
creepFound: Boolean(creep),
spawnOwned: spawn?.my ?? null,
creepOwned: creep?.my ?? null,
spawnBusy: Boolean(spawn?.spawning),
creepSpawning: creep?.spawning ?? null,
ticksToLive: creep?.ticksToLive ?? null,
bodySize: creep?.body.length ?? null,
hasClaim: creep
? creep.body.some(part => part.type === CLAIM)
: null,
boostedParts: creep
? getBoostedParts(creep).length
: null,
nearSpawn: spawn && creep
? creep.pos.isNearTo(spawn)
: null,
spawnEnergy: spawn
? spawn.store.getUsedCapacity(RESOURCE_ENERGY)
: null
}));
This command does not move, renew, or change Memory. Use it to confirm the selected objects and the destructive Boost consequence before enabling a mission.
Separate the decision from the action
function evaluateRenewRequest(input) {
const {
creepExists,
creepOwned,
creepSpawning,
ticksToLive,
renewThreshold,
targetTtl,
renewMissionActive,
hasClaimPart,
boostedPartCount,
allowBoostRemoval,
isNearSpawn,
spawnExists,
spawnOwned,
spawnActive,
spawnBusy,
spawnEnergy,
energyCost
} = input;
if (!creepExists || !spawnExists) {
return {
ready: false,
action: 'wait',
reason: 'object-missing'
};
}
if (!creepOwned || !spawnOwned) {
return {
ready: false,
action: 'stop',
reason: 'ownership-invalid'
};
}
if (!spawnActive) {
return {
ready: false,
action: 'wait',
reason: 'spawn-inactive'
};
}
if (creepSpawning) {
return {
ready: false,
action: 'wait',
reason: 'creep-spawning'
};
}
if (
!Number.isFinite(ticksToLive)
|| !Number.isFinite(renewThreshold)
|| !Number.isFinite(targetTtl)
|| renewThreshold < 0
|| targetTtl <= renewThreshold
) {
return {
ready: false,
action: 'stop',
reason: 'ttl-policy-invalid'
};
}
if (ticksToLive >= targetTtl) {
return {
ready: false,
action: 'work',
reason: 'target-ttl-reached'
};
}
if (
renewMissionActive !== true
&& ticksToLive > renewThreshold
) {
return {
ready: false,
action: 'work',
reason: 'ttl-sufficient'
};
}
if (hasClaimPart) {
return {
ready: false,
action: 'replace',
reason: 'claim-part-present'
};
}
if (
boostedPartCount > 0
&& allowBoostRemoval !== true
) {
return {
ready: false,
action: 'replace',
reason: 'boost-removal-not-confirmed'
};
}
if (!isNearSpawn) {
return {
ready: false,
action: 'move',
reason: 'move-to-spawn'
};
}
if (spawnBusy) {
return {
ready: false,
action: 'wait',
reason: 'spawn-busy'
};
}
if (
!Number.isFinite(spawnEnergy)
|| !Number.isFinite(energyCost)
|| spawnEnergy < energyCost
) {
return {
ready: false,
action: 'wait',
reason: 'spawn-energy-not-enough'
};
}
return {
ready: true,
action: 'renew',
reason: 'ready'
};
}
The pure decision can be tested with ordinary objects. It does not call the server API, remove Boosts, or reserve the Spawn.
Complete renewal mission
State impact: this script writes creep.memory.renewing, may move Worker1 toward Spawn1, and may submit repeated renewal actions until the configured target TTL is reached. A successful renewal consumes Spawn Energy, occupies the Spawn action, increases TTL after tick processing, and removes all Boosts.
function getBodyCost(body) {
return body.reduce((total, part) => {
const cost = BODYPART_COST[part.type];
if (!Number.isFinite(cost)) {
throw new TypeError(
'Unknown body part: ' + String(part.type)
);
}
return total + cost;
}, 0);
}
function getRenewStep(body) {
if (!Array.isArray(body) || body.length === 0) {
return null;
}
const bodyCost = getBodyCost(body);
return {
bodySize: body.length,
bodyCost,
addedTicks: Math.floor(600 / body.length),
energyCost: Math.ceil(
bodyCost / 2.5 / body.length
)
};
}
function runRenewMission(input) {
const {
spawn,
creep,
renewThreshold,
targetTtl,
allowBoostRemoval
} = input;
if (!spawn || !creep) {
return {
status: 'object-missing'
};
}
if (spawn.my !== true || creep.my !== true) {
return {
status: 'ownership-invalid'
};
}
if (!spawn.isActive()) {
return {
status: 'spawn-inactive'
};
}
if (creep.spawning) {
return {
status: 'creep-spawning'
};
}
if (
!Number.isFinite(renewThreshold)
|| !Number.isFinite(targetTtl)
|| renewThreshold < 0
|| targetTtl <= renewThreshold
) {
return {
status: 'ttl-policy-invalid'
};
}
const step = getRenewStep(creep.body);
if (!step) {
return {
status: 'body-invalid'
};
}
if (creep.body.some(part => part.type === CLAIM)) {
return {
status: 'claim-creep-must-be-replaced'
};
}
const boostedPartCount = creep.body.filter(part =>
typeof part.boost === 'string'
).length;
if (
boostedPartCount > 0
&& allowBoostRemoval !== true
) {
return {
status: 'boost-removal-not-confirmed',
boostedPartCount
};
}
if (!Number.isFinite(creep.ticksToLive)) {
return {
status: 'ttl-unavailable'
};
}
if (creep.ticksToLive >= targetTtl) {
creep.memory.renewing = false;
return {
status: 'target-ttl-reached',
ticksToLive: creep.ticksToLive
};
}
if (
creep.memory.renewing !== true
&& creep.ticksToLive > renewThreshold
) {
return {
status: 'ttl-sufficient',
ticksToLive: creep.ticksToLive
};
}
if (creep.ticksToLive <= renewThreshold) {
creep.memory.renewing = true;
}
if (!creep.pos.isNearTo(spawn)) {
const moveResult = creep.moveTo(spawn, {
range: 1,
reusePath: 10
});
return {
status: 'moving-to-spawn',
moveResult,
step
};
}
if (spawn.spawning) {
return {
status: 'spawn-busy',
step
};
}
const spawnEnergy = spawn.store.getUsedCapacity(
RESOURCE_ENERGY
);
if (
!Number.isFinite(spawnEnergy)
|| spawnEnergy < step.energyCost
) {
return {
status: 'spawn-energy-not-enough',
spawnEnergy,
step
};
}
const ticksToLiveBefore = creep.ticksToLive;
const result = spawn.renewCreep(creep);
return {
status: result === OK
? 'renew-submitted'
: 'renew-failed',
result,
step,
ticksToLiveBefore
};
}
module.exports.loop = function () {
const spawn = Game.spawns.Spawn1;
const creep = Game.creeps.Worker1;
const outcome = runRenewMission({
spawn,
creep,
renewThreshold: 300,
targetTtl: 1200,
allowBoostRemoval: false
});
if (outcome.status === 'renew-failed') {
console.log(JSON.stringify({
type: 'renew-creep-failed',
spawnName: spawn?.name ?? null,
creepName: creep?.name ?? null,
...outcome
}));
}
};
The example intentionally renews only one named Creep at one named Spawn. A real queue must decide which renewal or spawn request has priority.
Source correction: a renewal mission must remember that it has started. Checking only whether the current TTL is above the start threshold would stop the mission immediately after the first successful step. The published code uses creep.memory.renewing so the Creep continues until targetTtl, then clears the flag.
Renewal competes with spawning
The Spawn must not be busy creating another Creep. Renewal and replacement production compete for the same structure:
renew the current Creep
OR
spawn a replacement or another required role
Do not let independent modules control the same Spawn without a shared priority decision. Emergency harvesting and defense generally outrank renewing a non-critical convenience role.
When to renew and when to replace
| Renewal may fit | Replacement may fit |
|---|---|
| The Creep works close to the Spawn. | The Creep is in a remote room. |
| The existing body still matches the task. | A newer body design is needed. |
| The Spawn has spare time. | The Spawn queue requires centralized timing. |
| No important Boost must be preserved. | The Creep has CLAIM or valuable Boosts. |
| Leaving work briefly is acceptable. | A smooth handoff is required. |
Renewal is not the default best choice. It trades Spawn time, Energy, travel, and possible Boost loss for continued use of the current body.
Return-code troubleshooting
| Code | Likely cause | Action |
|---|---|---|
OK | The renewal was scheduled. | Read TTL and Spawn Energy on the next tick. |
ERR_NOT_OWNER | The Spawn or Creep is not yours. | Validate both objects. |
ERR_BUSY | The Spawn is creating another Creep. | Use one shared Spawn queue. |
ERR_NOT_ENOUGH_ENERGY | The Spawn lacks enough Energy. | Compare its Store with the calculated step cost. |
ERR_INVALID_TARGET | The target is not a Creep or contains CLAIM. | Check the object and full body. |
ERR_FULL | The target timer cannot be increased further. | Stop at a lower target TTL. |
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 Controller and isActive(). |
Verify TTL and Energy on the next tick
OK means the operation was scheduled. The current object properties are not a verified post-action state during the same script execution. Record a before snapshot, then inspect the following tick:
console.log(JSON.stringify({
tick: Game.time,
creepName: creep.name,
ticksToLive: creep.ticksToLive,
spawnEnergy: spawn.store.getUsedCapacity(
RESOURCE_ENERGY
),
boostedParts: getBoostedParts(creep).length
}));
This article has not performed a live renewal or Boost-removal test, so those Verification fields remain Pending.
Debugging checklist
- Confirm the Spawn and Creep names, ownership, and current visibility.
- Reject a Creep that is still spawning.
- Calculate one renewal step from the complete body.
- Reject any body containing CLAIM.
- Require explicit approval before removing Boosts.
- Choose both a return threshold and a target TTL.
- Move to an adjacent square and save the movement result.
- Check that the Spawn is active and not spawning.
- Check the selected Spawn's own Energy Store.
- Save the
renewCreep()result and verify the next tick. - Coordinate renewal with emergency and replacement spawning.
- Use the error-code reference for shared constants.
Scope and next steps
This guide does not implement a multi-Creep renewal queue, compare long-term resource efficiency, reserve traffic around the Spawn, reapply Boosts, route remote Creeps home, renew Power Creeps, or schedule several Spawns. Continue with safe Creep recycling.
Frequently asked questions
How many ticks does one renewal add?
floor(600 / body size), if the API accepts the action and the timer is not full.
Does renewal use Extension Energy?
This guide checks the selected Spawn's Store because the official method requires the Spawn to have enough Energy.
Can renewal preserve Boosts?
No. The method removes all Boosts.
Should every low-TTL Creep be renewed?
No. Travel, CLAIM, Boosts, queue pressure, body upgrades, and handoff requirements often favor replacement.