OBSERVABILITY · RATE-LIMITED NOTIFICATIONS

Send Screeps Notifications Without Spamming Every Tick

CURATED PATH

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

Debugging / Observability

Recommended next steps

Isolate failures before alerting on themPrerequisite governanceKeep Room-event context with an incidentEvent sourceCheck CPU pressure behind noisy incidentsRuntime metric
Verification statusChinese source article: Reviewed in full · Official API: Checked August 30, 2026 — Game.notify message limit, 20 calls per tick, Simulation boundary, and groupInterval minutes · Public engine source: Checked screeps/engine 80977824199a596d174d392fd0cf8c458c21fcbd — notify intent queue cap 20 with current OK / ERR_FULL results

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official API
Checked August 30, 2026 — Game.notify message limit, 20 calls per tick, Simulation boundary, and groupInterval minutes
Public engine source
Checked screeps/engine 80977824199a596d174d392fd0cf8c458c21fcbd — notify intent queue cap 20 with current OK / ERR_FULL results
JavaScript syntax
Passed — 3/3 article examples
Evidence boundary
Checked — local scheduling is not described as external email delivery
Screeps Console test
Pending
Live grouped-email and 20-call saturation test
Pending
External inbox delivery observation
Pending
Evidence level
Official documentation review, public engine-source review, repository review, and offline JavaScript syntax only
Last editorial review
August 30, 2026

Send one notification first

Game.notify(message, groupInterval) sends a custom notification to the email channel configured for your Screeps profile. Start with one small call, then add rate limiting only when the condition can stay true across many ticks.

const result = Game.notify(
  'A Screeps condition needs attention.',
  30
);

console.log(result);

The official API documents a maximum message length of 1,000 characters, up to 20 notification calls in one game tick, and no availability in the Simulation Room. A groupInterval of 0 schedules immediately; a positive value groups notifications using that many minutes.

The public API page does not currently document a return-code table for Game.notify(). In the checked screeps/engine snapshot 80977824199a596d174d392fd0cf8c458c21fcbd, the method returns OK when the notification intent enters the per-tick queue and ERR_FULL when that 20-intent queue is full. Treat that return-code detail as an engine implementation boundary, not as a stronger API promise than the documentation makes.

Know the two different rate controls

ControlWhat it doesWhat it does not do
20 calls per tickOfficial per-tick scheduling limit.It does not stop one persistent condition from calling again on every later tick.
groupIntervalGroups email notifications over a number of minutes.It is not a replacement for deciding when your script should call Game.notify().
Memory incident stateProject policy that suppresses repeated calls for the same unresolved condition.It is not an official Screeps notification limit.

For most alerts, the useful pattern is: call once when the condition becomes active, stay quiet while it remains active, and allow a new notification after the condition clears. Add a repeat interval only when repeated reminders are genuinely useful.

Preserve the call result

function sendNotification(message, groupInterval = 0) {
  if (
    typeof message !== 'string'
    || message.length === 0
    || message.length > 1000
  ) {
    return { status: 'invalid-message' };
  }
  if (!Number.isFinite(groupInterval) || groupInterval < 0) {
    return { status: 'invalid-group-interval' };
  }

  const result = Game.notify(message, groupInterval);
  return {
    status: result === OK
      ? 'notification-scheduled'
      : 'notification-not-scheduled',
    result
  };
}

The length and non-negative interval checks are local guardrails around the documented parameters. The result === OK branch follows the checked engine behavior above. Preserve the raw result instead of turning every normal return into “delivered.”

Notify once per incident instead of once per tick

The helper below stores only the state needed for a recurring condition. active comes from your own alert rule. When the condition clears, the incident resets. A positive repeatAfterTicks is an optional project policy for reminders; it is measured in game ticks and is separate from the email-oriented groupInterval minutes.

function notifyIncident({
  key,
  active,
  message,
  groupInterval = 0,
  repeatAfterTicks = null
}) {
  if (typeof key !== 'string' || key.length === 0) {
    return { status: 'invalid-incident-key' };
  }

  Memory.notificationIncidents ??= {};
  const state = Memory.notificationIncidents[key] ??= {
    active: false,
    lastScheduledAt: null,
    lastResult: null
  };

  if (!active) {
    state.active = false;
    return { status: 'incident-clear' };
  }

  const repeatDue = Number.isInteger(repeatAfterTicks)
    && repeatAfterTicks > 0
    && Number.isInteger(state.lastScheduledAt)
    && Game.time - state.lastScheduledAt >= repeatAfterTicks;

  if (state.active && !repeatDue) {
    return { status: 'already-reported' };
  }

  const submission = sendNotification(message, groupInterval);
  state.lastResult = submission.result ?? null;

  if (submission.status === 'notification-scheduled') {
    state.active = true;
    state.lastScheduledAt = Game.time;
  }

  return submission;
}

Do not copy a made-up room name or threshold into production. Pass a stable incident key, the condition you already calculated, and a message built from the real object or room you are monitoring. If the checked engine returns ERR_FULL, this helper leaves the incident eligible for a later retry instead of marking it as reported.

Scheduled is not externally delivered

Keep the evidence levels separate:

ObservationWhat it supportsWhat it does not prove
Your condition became trueThe local alert rule fired.That Game.notify() was called.
Checked engine returns OKThe notify intent entered the engine's per-tick queue.That an external email arrived.
Checked engine returns ERR_FULLThe checked per-tick notify queue was full.That the message will be retried automatically.
Email or external inbox observationExternal delivery evidence for that environment.A general timing guarantee for every future notification.

groupInterval can intentionally delay grouped mail. Game code has no documented inbox receipt, so do not name a Memory state delivered merely because the local call was accepted.

Optional hardening for many notification producers

If several modules can notify in the same tick, give them one shared local budget instead of letting every module assume it owns all 20 official slots. A production bot can choose a smaller local limit to reserve headroom.

function createNotificationBudget(maximumCalls = 20) {
  const limit = Math.min(
    20,
    Math.max(0, Number.isInteger(maximumCalls)
      ? maximumCalls
      : 20)
  );
  let scheduled = 0;

  return {
    send(message, groupInterval = 0) {
      if (scheduled >= limit) {
        return { status: 'local-budget-exhausted' };
      }

      const submission = sendNotification(message, groupInterval);
      if (submission.status === 'notification-scheduled') {
        scheduled += 1;
      }
      return submission;
    },
    getScheduledCount() {
      return scheduled;
    }
  };
}

Create one budget object for the tick and route notification-producing modules through it. This is a project coordinator, not an extra Screeps API rule. Payload signing, approval workflows, revision ledgers, and external alert-service guarantees belong in a separate operations system when your threat model actually requires them.

Common mistakes

  • Calling every tick while a condition stays true: use incident state so one unresolved condition does not keep scheduling calls.
  • Treating groupInterval as a script-side cooldown: it controls email grouping in minutes; your own Memory state controls whether you call again.
  • Claiming delivery from a local result: accepted scheduling and external email observation are different evidence levels.
  • Testing in the Simulation Room: the official API says Game.notify() is not available there.
  • Scattered per-module call counting: use one shared coordinator when many producers can notify in the same tick.
  • Overengineering a single alert: start with one call and one incident flag; add queues or richer identity only when the system actually needs them.

Evidence and production boundary

The current article was checked against the official Game.notify() API and the public screeps/engine snapshot 80977824199a596d174d392fd0cf8c458c21fcbd. The three JavaScript examples were syntax-checked offline. No real Screeps Console notification, 20-call saturation run, grouped-email timing trace, or inbox-delivery observation is claimed.

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.