SPAWN · RETURN-CODE DEBUGGING

How to Diagnose spawnCreep() Return Codes

CURATED PATH

These links are maintained for a concrete prerequisite, failure mode, debugging step, or next lesson instead of generic topic similarity.

Spawn

Recommended next steps

spawnCreep basicsPrimary API guideUsing Screeps Arena? Follow the Arena spawnCreep contractGame mode boundaryBlocked Spawn exitsConcrete failureDiagnosticsContinue debugging
Verification statusChinese source article: Existing bilingual mapping retained; this English-only pass did not use the Chinese page as live-game evidence · Official documentation: Checked August 17, 2026 — StructureSpawn.spawnCreep() body/name bounds, memory timing, energyStructures ordering, dryRun, all documented return codes, and Structure.isActive() · Static code review: Passed — explicit Energy-source context no longer relies on room.energyAvailable alone; body-cost diagnostics fail closed on unknown parts; Memory presence is not treated as spawn completion; retry classes remain return-code specific

VERIFICATION

Evidence and test status

Chinese source article
Existing bilingual mapping retained; this English-only pass did not use the Chinese page as live-game evidence
Official documentation
Checked August 17, 2026 — StructureSpawn.spawnCreep() body/name bounds, memory timing, energyStructures ordering, dryRun, all documented return codes, and Structure.isActive()
Static code review
Passed — explicit Energy-source context no longer relies on room.energyAvailable alone; body-cost diagnostics fail closed on unknown parts; Memory presence is not treated as spawn completion; retry classes remain return-code specific
Evidence level
Official-documentation review and static code review only; no real-shard execution is claimed
Console test pending
Not run in this editorial pass
Screeps Console test
Pending — no real-shard Console transcript was collected for this revision
Live multi-tick verification pending
No live Spawn request sequence, explicit-energyStructures failure, or accepted-to-complete spawning trace was collected for this revision
Last editorial review
August 17, 2026

Use this guide when

Use this guide after StructureSpawn.spawnCreep() returns a non-OK code and you need to decide whether the request should wait, change, or fail closed. If you are making your first simple Creep, use the beginner spawn lesson; this page is the return-code troubleshooting layer.

Current return-code boundary

ReturnDocumented boundaryUseful first check
OKThe spawn operation was scheduled successfully.Observe spawn.spawning; do not call the Creep “finished” yet.
ERR_NOT_OWNERYou do not own this Spawn.spawn.my.
ERR_NAME_EXISTSA Creep already has that name.Game.creeps[name] and your naming policy.
ERR_BUSYThe Spawn is already spawning another Creep.spawn.spawning.
ERR_NOT_ENOUGH_ENERGYThe energy sources available to this request do not contain enough Energy for the body.Body cost plus default or explicit energyStructures.
ERR_INVALID_ARGSThe body is invalid or the required name was not provided.Body length/constants and name.
ERR_RCL_NOT_ENOUGHThe current Room Controller level is insufficient to use this Spawn.spawn.isActive() and current RCL.

Do not add undocumented meanings to a code just because two failures happen near the same subsystem. In particular, ERR_RCL_NOT_ENOUGH from spawnCreep() is a Spawn-usage/RCL boundary, not a generic “too many Spawns” response.

Interpret ERR_RCL_NOT_ENOUGH precisely

if (spawn.isActive() !== true) {
  console.log(JSON.stringify({
    type: 'spawn-inactive',
    roomName: spawn.room.name,
    rcl: spawn.room.controller?.level ?? null
  }));
}

The base Structure API says isActive() is false when the Room Controller level is insufficient to use the structure. That makes it a useful diagnostic companion to ERR_RCL_NOT_ENOUGH. Do not rewrite the return code as a construction-limit error.

Diagnose ERR_NOT_ENOUGH_ENERGY with request context

Without energyStructures, the spawn request can draw the required Energy from Spawns and Extensions in the room. If you provide an explicit energyStructures array, those structures define the request's Energy sources and are used in the supplied order.

This matters because room.energyAvailable can look sufficient while an intentionally restricted request still returns ERR_NOT_ENOUGH_ENERGY.

function bodyCost(body) {
  if (!Array.isArray(body)) return null;

  let total = 0;
  for (const part of body) {
    const cost = BODYPART_COST[part];
    if (!Number.isFinite(cost)) return null;
    total += cost;
  }
  return total;
}

function describeSpawnEnergy(spawn, opts = {}) {
  const explicit = Array.isArray(opts.energyStructures);
  const selected = explicit ? opts.energyStructures : [];
  const selectedEnergyAvailable = explicit
    ? selected.reduce(
        (sum, structure) =>
          sum + (
            structure?.store?.getUsedCapacity(
              RESOURCE_ENERGY
            ) ?? 0
          ),
        0
      )
    : null;

  return {
    mode: explicit ? 'explicit' : 'room-default',
    roomEnergyAvailable: spawn.room.energyAvailable,
    selectedEnergyAvailable,
    selectedStructureIds: explicit
      ? selected.map(structure => structure?.id ?? null)
      : null
  };
}

console.log(JSON.stringify({
  bodyCost: bodyCost(body),
  energy: describeSpawnEnergy(spawn, opts)
}));

When energyStructures is explicit, selectedEnergyAvailable is a useful snapshot of those supplied Stores; it is not a replacement implementation of spawnCreep() validation. Invalid structures, ownership/RCL state, busy state, name collisions, and changes between checks still belong to the API result. Without an explicit list, room.energyAvailable remains useful context for the default room-wide Spawn/Extension pool.

Validate body and name without inventing API rules

The current API documents a body of 1–50 valid body-part constants and a name with a maximum length of 100 characters. Keep local validation narrow enough that it does not reject API-valid requests for reasons you invented.

function validateSpawnShape(body, name) {
  const validParts = new Set([
    WORK, MOVE, CARRY, ATTACK,
    RANGED_ATTACK, HEAL, TOUGH, CLAIM
  ]);

  if (
    !Array.isArray(body)
    || body.length < 1
    || body.length > 50
    || body.some(part => !validParts.has(part))
  ) {
    return { valid: false, reason: 'body-invalid' };
  }

  if (typeof name !== 'string' || name.length < 1 || name.length > 100) {
    return { valid: false, reason: 'name-invalid' };
  }

  return { valid: true, reason: 'shape-valid' };
}

Do not try to reproduce every runtime condition in the validator. Ownership, busy state, RCL, name collision, and Energy can change independently and should still be handled from the actual API result.

Do not over-restrict opts.memory

The optional memory field is documented as any. A role object such as { role: 'harvester' } is a common and readable project convention, but it is not an API rule that Memory must be a non-null plain object.

const opts = {
  memory: {
    role: 'harvester',
    homeRoom: spawn.room.name
  }
};

If your own bot requires an object schema, label that as a project contract and validate it at that boundary. Do not present it as a spawnCreep() restriction.

The API also documents supplied Creep memory as being stored immediately in Memory.creeps[name]. That makes a Memory entry useful state, but not evidence that the Creep has finished spawning. Use the real spawnCreep() result and spawn.spawning for the operation boundary; use the beginner spawn lesson for the full multi-tick spawn lifecycle.

Keep dryRun and the real call separate

dryRun: true checks whether the operation is possible at that moment without starting spawning. It does not reserve the name, Spawn, or Energy for a later call.

const dryRunResult = spawn.spawnCreep(
  body,
  name,
  { ...opts, dryRun: true }
);

let spawnResult = null;

if (dryRunResult === OK) {
  spawnResult = spawn.spawnCreep(
    body,
    name,
    opts
  );
}

console.log(JSON.stringify({
  tick: Game.time,
  dryRunResult,
  spawnResult
}));

Keep both values. If the dry run succeeds and the real call does not, the correct conclusion is not “Screeps contradicted itself.” It means the two calls are separate checks/submissions and your diagnostic should preserve the exact state and results rather than overwrite one with the other.

A useful diagnostic wrapper

function requestCreep(spawn, body, name, opts = {}) {
  if (!spawn) {
    return { status: 'spawn-missing' };
  }

  const shape = validateSpawnShape(body, name);
  if (!shape.valid) {
    return { status: shape.reason };
  }

  const dryRunResult = spawn.spawnCreep(
    body,
    name,
    { ...opts, dryRun: true }
  );

  if (dryRunResult !== OK) {
    return {
      status: 'dry-run-rejected',
      dryRunResult,
      spawnActive: spawn.isActive(),
      busy: Boolean(spawn.spawning),
      bodyCost: bodyCost(body),
      energy: describeSpawnEnergy(spawn, opts)
    };
  }

  const spawnResult = spawn.spawnCreep(
    body,
    name,
    opts
  );

  return {
    status: spawnResult === OK
      ? 'spawn-request-accepted'
      : 'spawn-request-rejected',
    dryRunResult,
    spawnResult,
    spawnActive: spawn.isActive(),
    busy: Boolean(spawn.spawning),
    bodyCost: bodyCost(body),
    energy: describeSpawnEnergy(spawn, opts)
  };
}

This wrapper is diagnostic, not a full spawn queue. A production scheduler still needs demand, body policy, replacement timing, unique names, and retry rules.

Do not retry every error the same way

CodeTypical policy class
ERR_BUSYWait for the Spawn to become free.
ERR_NOT_ENOUGH_ENERGYWait, reduce the body, or change the explicit Energy-source policy.
ERR_NAME_EXISTSFirst decide whether that name already represents the unit your scheduler wanted. Reuse the existing assignment when appropriate; generate another name only when you truly need another Creep.
ERR_INVALID_ARGSFix the request; blind retry will repeat the same invalid input.
ERR_NOT_OWNERFix Spawn selection.
ERR_RCL_NOT_ENOUGHFix/await the RCL and structure-usability condition; do not treat it as an Energy retry.

What static review does not prove

This revision verifies the current documented return boundary and code branches offline. It does not contain a live Spawn transcript showing dry-run acceptance, a real submission, and later completion. Console and live multi-tick verification remain pending.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide keeps the technical scope and verification boundaries of its Chinese source. The verification record shows which checks were completed and which runtime observations are still pending.