SPAWNING · RETURN-CODE DEBUGGING

How to Debug spawnCreep() Return Codes in Screeps

Validate the Spawn, Creep name, body, Energy, Memory, and optional structures; run dryRun first; preserve the real spawnCreep() result; and map each error code to a concrete fix.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — StructureSpawn.spawnCreep(), Room Energy, body and name limits · API boundary: dryRun checks current conditions but does not start spawning

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — StructureSpawn.spawnCreep(), Room Energy, body and name limits
API boundary
dryRun checks current conditions but does not start spawning
JavaScript syntax
Passed
Offline validation review
Passed — Spawn, body, name, Energy, Memory, dryRun and final result
Screeps Console test
Pending
Live Spawn contention test
Pending
Last verified
July 25, 2026

Quick answer

When StructureSpawn.spawnCreep() fails, save the return value instead of immediately repeating the call. Check five areas in order: the Spawn, the requested name, the body, currently available Spawn-and-Extension Energy, and the options object. Use dryRun: true for a non-mutating precheck, then save the real call's result separately because current conditions can still change later in the tick.

The five checks to run first

  1. The Spawn exists, belongs to you, is active, and is not already spawning.
  2. The requested name is a non-empty string of at most 100 characters and is not already present.
  3. The body is an array containing 1–50 official body-part constants.
  4. The room currently has enough Energy in its Spawns and Extensions.
  5. memory, energyStructures, and directions match the API contract.

This article is a debugging companion to the beginner spawnCreep() tutorial. It does not repeat the first-Creep walkthrough.

The basic spawnCreep() call

const result = Game.spawns.Spawn1.spawnCreep(
  [WORK, CARRY, MOVE],
  'Worker1',
  {
    memory: {
      role: 'worker'
    }
  }
);

console.log('spawnCreep() returned ' + result);

OK means the spawning process was accepted. It does not mean the Creep is complete in the current tick. Read spawn.spawning and later ticks to observe progress.

Validate a 1–50 part body

The current official API accepts the eight regular body-part constants and requires 1–50 elements:

const BODY_PARTS = new Set([
  WORK,
  MOVE,
  CARRY,
  ATTACK,
  RANGED_ATTACK,
  HEAL,
  TOUGH,
  CLAIM
]);

function validateBody(body) {
  if (!Array.isArray(body)) {
    return {
      valid: false,
      reason: 'body-not-array'
    };
  }

  if (body.length < 1 || body.length > 50) {
    return {
      valid: false,
      reason: 'body-length-invalid'
    };
  }

  for (const part of body) {
    if (!BODY_PARTS.has(part)) {
      return {
        valid: false,
        reason: 'unknown-body-part'
      };
    }
  }

  return {
    valid: true,
    reason: 'ready'
  };
}

A body that is syntactically valid may still be unsuitable for its role. Use the body-parts guide for ability design.

Calculate body cost from official constants

function getBodyCost(body) {
  return body.reduce((total, part) => {
    const cost = BODYPART_COST[part];

    if (!Number.isFinite(cost)) {
      throw new TypeError(
        'Unknown body part: ' + String(part)
      );
    }

    return total + cost;
  }, 0);
}

Compare the result with spawn.room.energyAvailable. That property is the current Energy in all Spawns and Extensions in the room. It does not include Storage, Terminal, Link, or Container inventory.

Validate the Creep name

function validateCreepName(name) {
  if (typeof name !== 'string') {
    return {
      valid: false,
      reason: 'name-not-string'
    };
  }

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

  if (Game.creeps[name]) {
    return {
      valid: false,
      reason: 'name-exists'
    };
  }

  return {
    valid: true,
    reason: 'ready'
  };
}

This precheck improves diagnostics but does not replace handling ERR_NAME_EXISTS. Another module can submit the same name later in the same tick.

What dryRun can and cannot prove

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

A dry run checks the current request without starting the Spawn. It can reveal a busy Spawn, duplicate name, insufficient Energy, malformed body or name, ownership problems, and RCL restrictions. It is not a lock. Code that runs afterward can still consume the Spawn, use the name, or change the Energy state.

Complete safe submission helper

State impact: local validation and the dry run do not mutate the game. The final spawnCreep() call can reserve Energy and start spawning one Creep.

const BODY_PARTS = new Set([
  WORK,
  MOVE,
  CARRY,
  ATTACK,
  RANGED_ATTACK,
  HEAL,
  TOUGH,
  CLAIM
]);

function getBodyCost(body) {
  return body.reduce((total, part) => {
    const cost = BODYPART_COST[part];

    if (!Number.isFinite(cost)) {
      throw new TypeError(
        'Unknown body part: ' + String(part)
      );
    }

    return total + cost;
  }, 0);
}

function validateSpawnRequest(input) {
  const { spawn, body, name, memory } = input;

  if (!spawn) {
    return { valid: false, reason: 'spawn-missing' };
  }

  if (spawn.spawning) {
    return { valid: false, reason: 'spawn-busy' };
  }

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

  if (Game.creeps[name]) {
    return { valid: false, reason: 'name-exists' };
  }

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

  if (
    memory !== undefined
    && (
      !memory
      || typeof memory !== 'object'
      || Array.isArray(memory)
    )
  ) {
    return { valid: false, reason: 'memory-invalid' };
  }

  const bodyCost = getBodyCost(body);

  if (spawn.room.energyAvailable < bodyCost) {
    return {
      valid: false,
      reason: 'energy-not-enough',
      bodyCost,
      energyAvailable: spawn.room.energyAvailable
    };
  }

  return {
    valid: true,
    reason: 'ready',
    bodyCost,
    energyAvailable: spawn.room.energyAvailable
  };
}

function submitSpawnRequest(input) {
  const validation = validateSpawnRequest(input);

  if (!validation.valid) {
    return {
      status: 'local-validation-failed',
      ...validation
    };
  }

  const { spawn, body, name, memory, directions } = input;
  const dryOptions = {
    memory,
    dryRun: true
  };

  if (Array.isArray(directions)) {
    dryOptions.directions = directions;
  }

  const dryRunResult = spawn.spawnCreep(
    body,
    name,
    dryOptions
  );

  if (dryRunResult !== OK) {
    return {
      status: 'dry-run-failed',
      dryRunResult,
      ...validation
    };
  }

  const actualOptions = { memory };

  if (Array.isArray(directions)) {
    actualOptions.directions = directions;
  }

  const result = spawn.spawnCreep(
    body,
    name,
    actualOptions
  );

  return {
    status: result === OK
      ? 'spawn-submitted'
      : 'spawn-failed-after-dry-run',
    dryRunResult,
    result,
    ...validation
  };
}

module.exports.loop = function () {
  const spawn = Game.spawns.Spawn1;
  const desiredName = 'Worker1';

  if (Game.creeps[desiredName]) {
    return;
  }

  const outcome = submitSpawnRequest({
    spawn,
    body: [WORK, CARRY, MOVE],
    name: desiredName,
    memory: {
      role: 'worker',
      memoryVersion: 1
    },
    directions: [TOP, RIGHT, BOTTOM, LEFT]
  });

  if (outcome.status !== 'spawn-submitted') {
    console.log(JSON.stringify({
      type: 'spawn-request-failed',
      spawnName: spawn?.name ?? null,
      creepName: desiredName,
      ...outcome
    }));
  }
};

Return-code table

CodeLikely causeNext check
OKThe Spawn accepted the request.Inspect spawn.spawning on later ticks.
ERR_NOT_OWNERThe Spawn is not yours.Check ownership and selected object.
ERR_NAME_EXISTSThe name is already used.Inspect Game.creeps and your naming strategy.
ERR_BUSYThe Spawn is already spawning.Coordinate all spawn callers.
ERR_NOT_ENOUGH_ENERGYSpawn and Extension Energy is insufficient.Compare body cost with current energyAvailable.
ERR_INVALID_ARGSBody, name, directions, or options are malformed.Validate each argument independently.
ERR_RCL_NOT_ENOUGHThe Spawn is not active at the current RCL.Check Controller ownership, level, and spawn.isActive().

When to use energyStructures

By default, the method draws from Spawns and Extensions in the room. An explicit list controls which eligible structures are used and in what order:

const energyStructures = [
  spawn,
  ...spawn.room.find(FIND_MY_STRUCTURES, {
    filter: structure =>
      structure.structureType === STRUCTURE_EXTENSION
  })
];

const result = spawn.spawnCreep(
  body,
  name,
  {
    memory,
    energyStructures
  }
);

Do not include Storage or Containers. The option accepts Spawns and Extensions used for the spawning process.

Debugging checklist

  • Save the local validation, dry-run result, and final result separately.
  • Confirm the Spawn exists, is yours, is active, and is idle.
  • Validate body array length and every body-part constant.
  • Compute cost from BODYPART_COST.
  • Compare cost with current room.energyAvailable.
  • Validate name type, length, and uniqueness.
  • Use a plain object for initial Memory.
  • Validate optional directions and energy structures.
  • Coordinate all code that can call the same Spawn.
  • Use the error-code reference for shared constants.

Scope and next steps

This guide does not design quotas, replacement timing, role priorities, multi-Spawn scheduling, boost plans, or automatic names for a large colony. Continue with building a body from current Energy.

Frequently asked questions

Does dryRun start spawning?

No. It only checks the current request.

Why can the real call fail after a successful dry run?

Another caller can use the Spawn, name, or Energy later in the tick.

Does energyAvailable include Storage?

No. It represents current Energy in the room's Spawns and Extensions.

Can spawnCreep() return ERR_NOT_IN_RANGE?

No. That code is not part of the documented return set for this method.

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.