FOUNDATION · CONTAINER WITHDRAWAL

How to Make a Screeps Creep Withdraw Energy from a Container

CURATED PATH

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

Room Economy / Container

Recommended next steps

Keep long-lived Containers healthy before decay becomes urgentMaintenance next step
Verification statusChinese source article: Reviewed in full · Official docs: Checked — Creep.withdraw(), Room.find(), StructureContainer · API and constants: Checked

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — Creep.withdraw(), Room.find(), StructureContainer
API and constants
Checked
JavaScript syntax
Passed
Offline logic review
Passed — missing Creep, full Store, no Container, range, error logging
Live multi-tick room test
Pending
Official engine source
Checked August 16, 2026 — Creep.withdraw() amount validation, target checks, Store capacity, range, and intent submission
Amount boundary
Verified in source — omitted and falsy zero amount use the automatic min(free capacity, target amount) path; negative amount returns ERR_INVALID_ARGS
Processor boundary
Checked — actual withdraw amount can be truncated by current Creep free capacity or current target Store before the transfer event is recorded
Screeps Console test
Pending — no live zero-amount or competing-withdraw transcript is claimed
Live exact-amount verification
Pending — no real-shard Store delta or EVENT_TRANSFER trace is claimed
Last editorial review
August 16, 2026

Quick answer

Use creep.withdraw(container, RESOURCE_ENERGY) to move Energy from a Container into a Creep. First confirm that the Creep has free Store capacity and that the Container currently holds Energy. If the method returns ERR_NOT_IN_RANGE, move toward the Container and retry on a later tick. Save every return value so withdrawal and movement failures remain distinguishable.

withdraw() and transfer() use opposite directions

CallResource direction
creep.withdraw(container, RESOURCE_ENERGY)Container → Creep
creep.transfer(spawn, RESOURCE_ENERGY)Creep → Spawn

Use the delivery tutorial when the Creep already carries Energy. This article only handles acquisition from one Container.

Prerequisites and read-only checks

The example expects a Creep named Hauler1 with active CARRY and MOVE parts. Run this read-only Console check before changing the main loop:

const creep = Game.creeps.Hauler1;

console.log(JSON.stringify({
  creepFound: Boolean(creep),
  spawning: creep ? creep.spawning : null,
  activeCarry: creep ? creep.getActiveBodyparts(CARRY) : null,
  activeMove: creep ? creep.getActiveBodyparts(MOVE) : null,
  freeEnergyCapacity: creep
    ? creep.store.getFreeCapacity(RESOURCE_ENERGY)
    : null
}));

A body may contain a CARRY part while having no active CARRY ability after damage. The complete script checks active parts and Store capacity separately. Review WORK, CARRY, and MOVE when either value is unexpected.

Find a Container that currently has Energy

function findContainerWithEnergy(creep) {
  return creep.pos.findClosestByPath(
    FIND_STRUCTURES,
    {
      filter: structure =>
        structure.structureType === STRUCTURE_CONTAINER
        && structure.store.getUsedCapacity(
          RESOURCE_ENERGY
        ) > 0
    }
  );
}

The result can be null. A room may contain no Container, every Container may be empty, or no valid path may be available. Do not pass a missing result into withdraw().

Complete focused example

State impact: this script may move Hauler1 and withdraw Energy from one Container. It does not deliver the Energy afterward and does not write Memory.

function findContainerWithEnergy(creep) {
  return creep.pos.findClosestByPath(
    FIND_STRUCTURES,
    {
      filter: structure =>
        structure.structureType === STRUCTURE_CONTAINER
        && structure.store.getUsedCapacity(
          RESOURCE_ENERGY
        ) > 0
    }
  );
}

module.exports.loop = function () {
  const creep = Game.creeps.Hauler1;

  if (!creep) {
    console.log('Hauler1 was not found.');
    return;
  }

  if (creep.spawning) {
    return;
  }

  if (creep.getActiveBodyparts(CARRY) <= 0) {
    console.log('Hauler1 has no active CARRY part.');
    return;
  }

  const freeCapacity =
    creep.store.getFreeCapacity(RESOURCE_ENERGY);

  if (freeCapacity === null || freeCapacity <= 0) {
    return;
  }

  const container = findContainerWithEnergy(creep);

  if (!container) {
    console.log(
      'No reachable Container with Energy was found.'
    );
    return;
  }

  const withdrawResult = creep.withdraw(
    container,
    RESOURCE_ENERGY
  );

  if (withdrawResult === ERR_NOT_IN_RANGE) {
    const moveResult = creep.moveTo(container, {
      range: 1,
      reusePath: 5
    });

    if (
      moveResult !== OK
      && moveResult !== ERR_TIRED
    ) {
      console.log(
        'Hauler1 moveTo() returned '
        + moveResult
      );
    }

    return;
  }

  if (withdrawResult !== OK) {
    console.log(
      'Hauler1 withdraw() returned '
      + withdrawResult
    );
  }
};

The target is selected again on later ticks. Its Store can change because another Creep withdraws first, so the filter does not remove the need to inspect the actual API result.

Omitted, zero, and explicit withdraw amounts are different policy choices

For the current official engine, omitting amount makes withdraw() choose the smaller of the Creep's current free Store capacity and the target's current amount of that resource. A subtle implementation boundary is that an explicit numeric 0 is also falsy in this submission path, so it is treated like an omitted amount rather than as a request to withdraw nothing. Do not use amount: 0 as a validation probe.

Amount argumentCurrent submission behaviorRecommended project policy
OmittedAuto-select min(free capacity, target amount).Good for this focused Container example.
0Handled as falsy/omitted by the checked engine.Reject or normalize it in your own API wrapper if zero must mean “do nothing.”
NegativeERR_INVALID_ARGS.Reject before submission.
Positive explicit amountMust fit the Creep and be available in the target at submission time.Use when the task owns an exact requested quantity.

Submission amount is not final transfer proof. The processor rechecks current free capacity and current target Store state. If same-tick activity changes either value after the API call, the amount actually moved can be lower than the earlier request. Verify later Store deltas or the exact transfer event before claiming an exact amount.

Return-code troubleshooting

ResultMeaningAction
OKThe withdraw intent was accepted for this tick.Verify the later Store delta; same-tick changes can reduce the amount actually moved.
ERR_NOT_IN_RANGEThe target is not adjacent.Move toward it and retry later.
ERR_NOT_ENOUGH_RESOURCESThe target lacks the requested resource amount.Refresh target and Store data.
ERR_FULLThe Creep has no free capacity.Switch to a delivery task.
ERR_INVALID_TARGETThe object cannot be used as a withdrawal target.Check existence and target type.
ERR_BUSYThe Creep is still spawning.Wait until spawning finishes.
ERR_INVALID_ARGSThe resource type is invalid, or the checked engine received a negative amount.Validate the resource and require a positive explicit amount when your wrapper uses one; zero is a separate falsy/default boundary.

Use the English Screeps error-code reference when movement or a less common result needs separate diagnosis.

Verify Store changes on a later tick

Official debugging guidance notes that an accepted command is not the same as a verified final state. Record a before snapshot, submit the action, then inspect the next tick:

console.log(JSON.stringify({
  tick: Game.time,
  creepEnergy:
    creep.store.getUsedCapacity(RESOURCE_ENERGY),
  containerEnergy:
    container.store.getUsedCapacity(RESOURCE_ENERGY)
}));

Do not assume that the filtered amount remains unchanged. Another Creep may compete for the same Container during the tick.

Why this guide does not use working state

A working flag is useful when one Creep alternates between acquisition and delivery, but it is not an argument to withdraw(). This guide isolates target selection, capacity checks, action submission, range handling, and return-code logging. Add cross-tick task switching only after the single withdrawal works. See Screeps Memory basics.

Debugging checklist

  • Confirm the Creep name and capitalization.
  • Wait until the Creep finishes spawning.
  • Check for at least one active CARRY part.
  • Check free Store capacity before calling withdraw().
  • Filter for STRUCTURE_CONTAINER and positive Energy.
  • Handle a missing or unreachable target.
  • Save withdrawResult and moveResult separately.
  • Retry the action on later ticks instead of expecting one call to finish the trip.
  • Verify Creep and Container Store changes after the command.

Scope and next steps

This example does not choose among Containers by logistics priority, reserve targets for multiple haulers, withdraw from Storage, Tombstones, or Ruins, construct Containers, or decide where the Energy should be delivered. Continue with picking up dropped Energy to learn the separate API for ground resources.

Frequently asked questions

What direction does withdraw() move Energy?

It moves Energy from the target Container into the calling Creep.

Why does withdraw() return ERR_NOT_IN_RANGE?

The Creep must be adjacent. Move toward the target and retry on a later tick.

Why does withdraw() return ERR_FULL?

The Creep has no free Store capacity for the selected resource.

Does OK prove that Energy arrived?

No. It proves that the API accepted the command. Confirm the Store values after tick processing.

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.