FOUNDATION · MEMORY CLEANUP

How to Clean Dead Creep Memory Safely in Screeps

Compare Memory.creeps with Game.creeps, remove only confirmed dead-name entries, synchronize explicitly managed task indexes, summarize logs, and avoid TTL-based or global deletion mistakes.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — Game.creeps, Creep.memory, Memory · Deletion boundary: Only name-indexed structures explicitly managed by the script

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — Game.creeps, Creep.memory, Memory
Deletion boundary
Only name-indexed structures explicitly managed by the script
JavaScript syntax
Passed
Offline cleanup review
Passed — live names, dead names, missing objects, managed indexes, log truncation
Screeps Console test
Pending
Live death-and-replacement cycle
Pending
Last verified
July 25, 2026

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.creeps to find stale names.
  • Keep every name that exists in Game.creeps.
  • Do not delete based only on predicted TTL.
  • Never delete the complete Memory.creeps object.
  • 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.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide is rewritten for a focused search intent while preserving the technical scope and verification boundaries of its Chinese source. Live-room evidence is claimed only when the verification record says it exists.