If a value you store with $getWorkflowStaticData() is gone the next time you click Execute workflow, nothing is broken. According to the n8n documentation, static data is not available when testing a workflow. It is saved only when the workflow is published and called by its trigger or a webhook. This guide explains how I ran into it and how I designed around it.
The short answer
- Manual and test executions do not save static data. Production executions of a published workflow do.
- Static data is saved when an execution succeeds. n8n checks for changes and saves them if necessary.
- It works only in the JavaScript Code node, not in the Python one.
- It is marked as experimental. The documentation says the data should be small and that it may behave unreliably under high-frequency executions.
All four points come from the n8n static data documentation.
How it showed up in my workflow
I run a small workflow, on n8n 2.8.3 self-hosted, that reads my WordPress post sitemap every 15 minutes and sends me a Telegram message and an email when a new post URL appears. To avoid sending the same post twice, it keeps the list of URLs it has already seen in global static data.
Because manual runs do not save static data, a list stored this way cannot be tested by clicking Execute workflow repeatedly. I made two choices because of that.
- The first scheduled run only records the posts that already exist and sends nothing, so a fresh start does not flood me with alerts for every old post.
- I added a
testModesetting. When it is on, the workflow ignores the saved list and treats the most recent URL as new, so I can check the Telegram and email steps without publishing a post. I turn it off before publishing the workflow.
After I published the workflow, a Telegram alert for a newly published post arrived, which is what I wanted to see.
A minimal check, and what I observed
To see the difference myself, I built a two-node workflow: a Schedule Trigger set to run every minute, followed by a JavaScript Code node that counts its own runs in global static data.
const sd = $getWorkflowStaticData('global');
sd.runs = (sd.runs || 0) + 1;
sd.lastRun = new Date().toISOString();
return [{ json: { runs: sd.runs, lastRun: sd.lastRun, mode: String($execution && $execution.mode) } }];- I clicked Execute workflow three times.
- I published the workflow and let the schedule run.
- I opened the Executions list and read the output of the Code node for individual runs.
This is what the node returned on 2026-10-19 on n8n 2.8.3, self-hosted.
| Run | mode | runs |
|---|---|---|
| Manual run (the third of three) | test | 1 |
| First scheduled run after publishing | production | 1 |
| Second scheduled run | production | 2 |
| Third scheduled run | production | 3 |
- The third manual run still returned
runs1. If the earlier manual runs had been saved, it would have been 3. - The first scheduled run also started at 1, so the manual runs did not carry over into the published workflow.
- The second and third scheduled runs returned 2 and 3, so the value was kept between runs of the published workflow.
$execution.modereturnedtestfor the manual run andproductionfor scheduled runs, which is a handy way to see which kind of run you are looking at.
This matches what the documentation says. Note what I recorded: I read the outputs of the third manual run and of the first three scheduled runs. I did not write down the outputs of the first two manual runs or of later scheduled runs.
What to use instead
The documentation recommends data tables for keeping data between executions. It says they work in test executions and do not require a Code node: you read a value with the Data Table node’s Get row operation and write it with Upsert row. I did not use data tables in this workflow, so I cannot tell you how they behave beyond what the documentation says.
What I did not test
- I tested only a one-minute schedule and looked at three scheduled runs, so I cannot say anything about long-running or high-frequency workflows.
- I did not test data tables.
- I did not test queue mode or multiple workers.
- I tested nothing on versions other than 2.8.3, and the documentation wording may change between versions.
Source: n8n documentation, static data in workflows. I read it on 2026-10-19 and ran the check described above on the same day.
수동 실행의 세 번째 결과도 1이고 예약 실행에서만 1→2→3으로 이어진 비교가 문제를 이해하기 쉽네요. 첫 예약 실행에서는 기존 URL만 기준으로 잡고 알림을 보내지 않는 설계도 초기 알림 폭주를 막는 데 실용적입니다. 텔레그램은 성공하고 이메일은 실패한 경우에는 다음 실행의 중복 알림을 어떻게 다루실지도 궁금합니다. URL 감지 상태와 채널별 전달 상태를 나눠 확인하는 후속 실험도 유용할 것 같습니다.
블로그 글을 읽고 좋은 의견을 남겨주셔서 감사합니다. 아쉽게도 이번 글에서는 텔레그램 알림은 성공하고 이메일은 실패했을 때의 예외 처리나 채널별 상태 분리 방식에 대해서는 다루지 않았습니다. 관련 문제를 겪고 계시다면 사용 중인 운영체제, n8n 버전, 실행한 명령어 및 출력 결과(비밀 정보 제외), 그리고 정확한 에러 메시지를 공유해 주시기 바랍니다.