Workiva Scripting automatically monitors the resources used by each script run, including memory, CPU, disk usage, and run time. When a script exceeds its configured resources limits, Scripting attempts to stop it safely while maintaining platform stability. In addition, the system gives scripts time to clean up and exit before they are stopped completely, ensuring minimal disruption for users.
This article explains how Scripting handles termination scenarios, what details appear in the logs, and includes reference screenshots.
For details on all system limits, see Workiva Scripting limits.
Scenarios
| Scenario | Behavior | Log Details |
| Very short run | Successful run | No resource usage data recorded |
| Normal completion | Successful run | Full resource usage summary logged (unless too short to sample) |
| Exceeding Memory Limit | Graceful termination | Displays memory threshold exceeded, shows average and peak usage |
| Exceeding Disk Limit | Graceful termination | Displays disk threshold exceeded, shows average and peak usage |
| Exceeding Allocated Time | Graceful termination | Displays runtime exceeded and total time used |
| Manual cancellation | Graceful termination | Full resource release, resource summary logged |
| Unexpected termination | Ungraceful termination | Partial or incomplete usage data recorded |
By enforcing resource limits automatically and providing detailed logs (as shown in the screenshots below), Workiva Scripting ensures both platform reliability and transparency for developers building and optimizing automation scripts.
Termination when a run completes successfully
When a script completes normally without exceeding limits, the log includes a resource usage summary showing average and peak usage across memory, CPU, disk, and execution time. This information provides visibility into performance characteristics for successfully completed runs.
Note: If a script runs very quickly (for example, less than a 1 second), resource sampling may not occur. In those cases, no resource usage data will be reported in the log.
Script runs too fast to sample
If a script finishes too quickly, the monitor cannot capture usage data.
Script has a normal exit
If a script completes without exceeding any limits, resource usage statistics are fully displayed in the log.
Termination when limits are exceeded
If a script run exceeds its configured limits (for example, memory, disk usage, or allocated time), Scripting will:
- Attempt to stop the script safely—When limits are exceeded, Scripting first sends a termination signal (SIGTERM) to let the script clean up and exit within 15 seconds.
- If the script doesn’t exit within 15 seconds—Scripting sends a SIGKILL signal, which immediately stops the process without allowing any cleanup. This is a forceful termination, and the run will complete with a failed status (displayed in red in the log). This status indicates a Scripting System error.
- Record the reason for stopping—the log will show which limit was reached and summarize the script’s resource usage.
The next sections provide examples of what happens when a script exceeds limits without code to handle these signals. To learn how to modify your scripts to respond gracefully to these signals, see Handling system signals in scripts.
Memory limit exceeded
When a script exceeds the configured memory limit, the log includes the reason for termination and details about the exceeded threshold.
Disk usage limit exceeded
When a script exceeds the configured disk usage limit, the log includes the reason for termination and details on disk usage at the time the limit was reached.
Allocated time exceeded
When a script exceeds its configured allocated time, the log includes the reason for termination and total runtime recorded.
Termination when user cancels a run
If a user manually cancels a run, Scripting first sends an interrupt signal (SIGINT) to let the script know it should stop. The script has 15 seconds to clean up, save progress, or roll back before shutdown. After this 15-second grace period, the same sequence described above takes place: a termination signal (SIGTERM) is sent to allow another 15 seconds for the script to complete cleanup, followed by a final kill signal (SIGKILL) if the script still hasn’t exited.
The image below shows how the script logs appear when script that does not include code to handle the interrupt signal.
To learn how to modify your scripts to respond gracefully to these signals, see Handling system signals in scripts.
Unplanned terminations
In rare cases, such as when a script consumes memory faster than the Scripting system can monitor consumption, the system may terminate a script before detecting that a limit was exceeded. When this happens, Scripting logs an incomplete usage report, showing only the data points collected before termination.
Handling system signals in scripts
Signal handling lets your script close cleanly when it’s canceled or stopped by the system. By catching and responding to system signals, your script can release resources, save work, and exit gracefully (for example, when the status is reported as "complete") instead of being forcefully terminated.
When a script is stopped because a user cancels it or it exceeds platform limits, Scripting delivers interruption, termination, and kill signals. See the earlier sections to learn more about how interruption, termination, and kill signals are triggered.
Scripting signals and how to handle them
This section provides code blocks and a complete example that you can use to handle these signals.
You can keep your signal handlers small and handle cleanup in one place using a try/finally block that always runs at shutdown. This keeps scripts predictable and easy to maintain.
The overall structure for handling signals in your script is the same regardless of which signal is received:
- Top of file: import modules and define the handler
- Early initialization: register the handler before doing any work
-
Around your work: wrap the main code in a
try/finallyblock so cleanup always runs
User cancels the run (SIGINT)
This occurs when a user clicks Cancel to cancel the script run.
# --- Top of file ---
import signal, sys
# Tiny handler: request immediate, graceful exit
def _graceful_exit(signum, frame):
print("SIGINT received: cancellation requested. Cleaning up…")
raise SystemExit(0)
# --- Early initialization ---
signal.signal(signal.SIGINT, _graceful_exit)
Around your work (later in the file):
try:
# your long-running work here
...
finally:
# central place for closing files, flushing logs, etc.
print("Final cleanup before exit")
The image below shows how the script logs appear when the interrupt signal (SIGINT) is handled.
Handling KeyboardInterrupt directly
Workiva Scripting also raises KeyboardInterrupt when SIGINT is delivered to the main thread. You can catch this exception in your try block and call the same cleanup path defined by _graceful_exit().
try:
# your long-running work here
...
except KeyboardInterrupt:
print("KeyboardInterrupt received: cleaning up before exit…")
_graceful_exit(None, None)
finally:
# central place for closing files, flushing logs, etc.
print("Final cleanup before exit")
The image below shows how the script logs appear when the interrupt signal is handled via keyboard interrupt.
Scripting termination signal to stop after a grace period (SIGTERM)
Scripting asks your script to stop (e.g., after limits are exceeded). Your script has a short grace period to exit.
# --- Top of file ---
def _graceful_term(signum, frame):
print("SIGTERM received: platform requested termination. Finishing up…")
raise SystemExit(0)
# --- Early initialization ---
signal.signal(signal.SIGTERM, _graceful_term)
The image below shows how the script logs appear when the termination signal (SIGTERM) is handled.
Scripting kill signal to stop immediately (SIGKILL)
If your script doesn’t exit during the grace period, Scripting will send SIGKILL. You cannot catch SIGKILL. Exit promptly on SIGINT/SIGTERM so your finally block can run before a kill occurs.
The screenshots in the earlier sections show how the logs would look as a result of the kill signal.
Complete example (copy‑paste ready)
This template registers minimal handlers and centralizes cleanup in finally. Paste into Workiva Scripting and replace the marked area with your logic.
Where to add your code: Replace the sample loop under # ===== YOUR CODE STARTS HERE ===== with your actual tasks. Avoid very long uninterruptible calls so the script can hit the finally block before the platform escalates to SIGKILL.
"""Workiva Scripting template: minimal handlers + cleanup in finally
- Tiny handlers raise SystemExit(0) on SIGINT/SIGTERM
- Single finally block runs for cleanup regardless of how the script stops
- Keeps code small and predictable
"""
import signal
import time
# --- Signal handlers (keep tiny) ---
def _graceful_exit(signum, frame):
print("SIGINT received: cancellation requested. Cleaning up…")
raise SystemExit(0)
def _graceful_term(signum, frame):
print("SIGTERM received: platform requested termination. Finishing up…")
raise SystemExit(0)
# --- Register handlers BEFORE doing any heavy work ---
signal.signal(signal.SIGINT, _graceful_exit)
signal.signal(signal.SIGTERM, _graceful_term)
# --- Your script's main work ---
def main():
print("Starting work…")
# ===== YOUR CODE STARTS HERE =====
# Example long-running loop. Replace with your actual logic.
for i in range(1, 1_000_000):
# Simulate a unit of work (replace with real logic)
print(f"Working on item {i}…")
time.sleep(1)
# ===== YOUR CODE ENDS HERE =====
if __name__ == "__main__":
try:
main()
finally:
# Centralized cleanup that always runs (including on SIGINT/SIGTERM)
print("Final cleanup before exit")