Quick answer
A dead Creep is absent from the current Game.creeps object, while its name-indexed data may remain in Memory.creeps. Iterate Memory.creeps, keep every name that still exists in Game.creeps, and delete only the missing names. Synchronize only custom indexes that your project explicitly manages; never clear the entire Memory tree or delete every matching key by name.
Why dead Creep Memory can remain
Name exists in Memory.creeps
AND
no current Creep exists at Game.creeps[name]
→ the Creep-specific Memory entry is stale
Game.creeps describes your current Creeps. Memory.creeps is persisted data. The two collections do not automatically express the same lifecycle after a Creep dies.
Iterate Memory.creeps, not Game.creeps
This cannot discover dead names:
for (const name in Game.creeps) {
// Every name here belongs to a current Creep.
}
Start from the persistent keys instead:
for (const name in Memory.creeps) {
if (!Game.creeps[name]) {
delete Memory.creeps[name];
}
}
The current object is the evidence. A name is eligible for this cleanup only when it remains in the Creep Memory index but no current Creep has that name.
Minimal safe cleanup function
function cleanDeadCreepMemory() {
if (
!Memory.creeps
|| typeof Memory.creeps !== 'object'
) {
return [];
}
const removedNames = [];
for (const name of Object.keys(Memory.creeps)) {
if (Game.creeps[name]) {
continue;
}
delete Memory.creeps[name];
removedNames.push(name);
}
return removedNames;
}
Returning the removed names makes summary logging and explicit secondary cleanup easier than returning only a count.
Synchronize only explicitly managed indexes
A project may store additional name-indexed data:
Memory.creepTasks = {
Worker1: {
targetId: 'example-id'
}
};
Deleting Memory.creeps.Worker1 does not automatically delete Memory.creepTasks.Worker1. Add rules only for structures whose schema you own:
function removeCreepFromManagedIndexes(name) {
if (
Memory.creepTasks
&& typeof Memory.creepTasks === 'object'
) {
delete Memory.creepTasks[name];
}
if (
Memory.creepAssignments
&& typeof Memory.creepAssignments === 'object'
) {
delete Memory.creepAssignments[name];
}
}
Do not recursively scan all Memory and delete every key whose text matches the Creep name. Another module may use the same string with unrelated meaning.
Complete cleanup example
State impact: this script deletes stale entries from Memory.creeps and from the two custom name-indexed structures shown below. It logs one bounded summary and then continues to later room logic.
function removeCreepFromManagedIndexes(name) {
if (
Memory.creepTasks
&& typeof Memory.creepTasks === 'object'
) {
delete Memory.creepTasks[name];
}
if (
Memory.creepAssignments
&& typeof Memory.creepAssignments === 'object'
) {
delete Memory.creepAssignments[name];
}
}
function cleanDeadCreepMemory() {
if (
!Memory.creeps
|| typeof Memory.creeps !== 'object'
) {
return [];
}
const removedNames = [];
for (const name of Object.keys(Memory.creeps)) {
if (Game.creeps[name]) {
continue;
}
delete Memory.creeps[name];
removeCreepFromManagedIndexes(name);
removedNames.push(name);
}
return removedNames.sort();
}
module.exports.loop = function () {
const removedNames = cleanDeadCreepMemory();
if (removedNames.length > 0) {
console.log(JSON.stringify({
type: 'dead-creep-memory-cleanup',
tick: Game.time,
count: removedNames.length,
names: removedNames.slice(0, 20),
truncated: removedNames.length > 20
}));
}
// Run role counts, replacement spawning,
// and task assignment after cleanup.
};
Running cleanup before role counts and replacement decisions prevents later logic from counting stale names as current workers. The log proves only that the name is absent now; it does not reveal whether the Creep expired, died in combat, was recycled, or disappeared for another reason.
Every tick or periodically
Every-tick cleanup is simple and keeps replacement logic consistent. Larger systems may choose a lower frequency:
if (Game.time % 10 === 0) {
cleanDeadCreepMemory();
}
The number 10 is an example policy, not an official recommendation. Periodic cleanup means stale data can remain for several ticks, so role counts and spawn logic must tolerate that delay.
Why ticksToLive is not the deletion test
A Creep whose ticksToLive is near zero still exists in the current tick. Do not delete its Memory based on an earlier prediction:
if (creep.memory.lastTicksToLive === 1) {
delete Memory.creeps[creep.name];
}
The safe test used here is current existence:
if (!Game.creeps[name]) {
delete Memory.creeps[name];
}
Prepare for Creep name reuse
Both Game.creeps and Memory.creeps use the Creep name as a key. Before reusing an old name, make sure:
- the old Creep no longer exists;
- old Memory was cleaned or intentionally migrated;
- the new
spawnCreep()request supplies the current schema; - old task assignments cannot be inherited accidentally.
Cleanup reduces stale-state risk but does not replace a deliberate naming and spawning policy.
Pure-function offline test
Separate the deletion decision from the mutation:
function findDeadCreepNames(
memoryCreeps,
gameCreeps
) {
if (
!memoryCreeps
|| typeof memoryCreeps !== 'object'
) {
return [];
}
const live = gameCreeps
&& typeof gameCreeps === 'object'
? gameCreeps
: {};
return Object.keys(memoryCreeps)
.filter(name => !live[name])
.sort();
}
This pure function supports ordinary-object tests for live names, multiple dead names, missing collections, and stable order. Such tests do not simulate an actual Screeps death, Memory serialization, name recreation, or the following replacement cycle.
Debugging checklist
- Iterate
Memory.creepsto find stale names. - Keep every name that exists in
Game.creeps. - Do not delete based only on predicted TTL.
- Never delete the complete
Memory.creepsobject. - Clean only custom indexes whose schema your code owns.
- Run cleanup before role counting and replacement spawning.
- Use one bounded summary instead of one permanent log per deletion.
- Do not treat the cleanup event as a death-cause report.
- Give ID-based targets and shared queues their own invalidation rules.
- Review target restoration by ID for non-name caches.
Scope and next steps
This article covers Creep-name-indexed data only. It does not diagnose death causes, spawn replacements, transfer assignments, clean Power Creep data, invalidate arbitrary object IDs, synchronize shards, or migrate Memory schemas. The real death-and-replacement cycle remains pending live verification.
Frequently asked questions
Why is a name in Memory.creeps but not Game.creeps?
The current Creep is gone, but the persisted entry has not been deleted yet.
Should I iterate Game.creeps to find dead names?
No. Dead names are missing there. Iterate Memory.creeps and compare each name.
Can I delete Memory at ticksToLive 1?
No. The Creep still exists on that tick. Delete only after the name is absent from the current Game.creeps object.
Does one deletion clean all task data?
No. Clean only the additional name-indexed structures that your project explicitly manages.