Skip to content

fix(api): read the START node sys.timestamp from the UTC clock in the app runner - #45235

Open
Eapp1e wants to merge 1 commit into
langgenius:mainfrom
Eapp1e:fix/app-runner-sys-timestamp
Open

Eapp1e wants to merge 1 commit into
langgenius:mainfrom
Eapp1e:fix/app-runner-sys-timestamp

Conversation

@Eapp1e

@Eapp1e Eapp1e commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Summary

WorkflowAppRunner.prepare() seeds the START node system variables with timestamp=int(naive_utc_now().timestamp()). naive_utc_now() returns a naive UTC wall clock, and datetime.timestamp() reads naive values as process-local time, so sys.timestamp is offset by the host's UTC offset — 8 hours behind on a TZ=Asia/Shanghai deployment. sys.timestamp is handed to workflow authors as a Unix epoch, so a shifted value silently breaks any duration/expiry arithmetic built on it.

This is the second START-node bootstrap path. _setup_variable_pool() in api/services/workflow_service.py carried the same call and was corrected the same way in #44847, which left the app-runner path behind. The fix reuses the contract that already exists in api/libs/datetime_utils.py:

to_utc_timestamp(dt) — "Convert a datetime to Unix epoch seconds, assuming naive values are UTC. … Treat those values as UTC instead of interpreting them in the host timezone."

Nothing else changes: the value keeps the same type and the same field name, and UTC hosts see no difference.

Fixes #44987 (same class of naive-UTC epoch conversion; this is the app-runner call site that issue's sweep of api/fields, api/controllers and api/tasks did not cover).

Screenshots

Not applicable — backend value only.

Test plan

api/tests/unit_tests/core/app/apps/workflow/test_app_runner.py (new file, mirroring the production path per api/README.md#testing):

  • Builds a real WorkflowAppRunner and runs prepare() with the START-node collaborators patched, capturing the kwargs handed to build_system_variables.
  • Pins naive_utc_now to a known instant and runs the host clock at TZ=Asia/Shanghai (time.tzset()), then asserts the captured timestamp equals the epoch of that instant.
  • Skips on platforms without time.tzset (Windows); CI runs Linux, so the assertion is exercised there.

Before/after on this machine (host clock at UTC+8, TZ set to XXX-8, naive_utc_now pinned to 2024-05-01T00:00:00Z):

without the fix : timestamp = 1714492800  (expected 1714521600)  -> assertion fails
with the fix    : timestamp = 1714521600  (expected 1714521600)  -> passes

ruff check and ruff format --check are clean on both files with api/.ruff.toml (line-length 120).

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've verified the change and added or updated tests where meaningful regression risk justifies coverage.
  • I've updated the documentation accordingly.
  • I ran make lint && make type-check (backend) and vp staged (frontend) to appease the lint gods

From Claude Code

@Eapp1e
Eapp1e requested a review from QuantumGhost as a code owner November 1, 2026 07:33
@github-actions github-actions Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-11-01 09:56:30.397717370 +0000
+++ /tmp/pyrefly_pr.txt	2026-11-01 09:56:10.205515687 +0000
@@ -3708,6 +3708,8 @@
     --> tests/unit_tests/core/app/apps/workflow/test_app_generator.py:1529:25
 ERROR Argument `SimpleNamespace` is not assignable to parameter `graph_runtime_state` with type `RuntimeState | None` in function `core.app.apps.workflow.app_generator.WorkflowAppGenerator._generate_worker` [bad-argument-type]
     --> tests/unit_tests/core/app/apps/workflow/test_app_generator.py:1533:37
+ERROR Missing argument `user_from` in function `core.app.entities.app_invoke_entities.WorkflowAppGenerateEntity.__init__` [missing-argument]
+  --> tests/unit_tests/core/app/apps/workflow/test_app_runner.py:23:52
 ERROR Cannot index into `str` [bad-index]
   --> tests/unit_tests/core/app/apps/workflow/test_generate_response_converter.py:75:16
 ERROR Cannot index into `str` [bad-index]

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 81.60% 81.61% +0.00%
Strict coverage 81.25% 81.25% +0.00%
Typed symbols 85,642 85,645 +3
Untyped symbols 19,390 19,389 -1
Modules 4397 4398 +1

@codecov

codecov Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.66%. Comparing base (170dbae) to head (3413b07).
⚠️ Report is 5 commits behind head on main.

❌ Your project check has failed because the head coverage (68.66%) is below the adjusted base coverage (88.85%). You can increase the head coverage or adjust the Removed Code Behavior.

❗ There is a different number of reports uploaded between BASE (170dbae) and HEAD (3413b07). Click for more details.

HEAD has 3 uploads less than BASE
Flag BASE (170dbae) HEAD (3413b07)
api 4 1
Additional details and impacted files
@@             Coverage Diff             @@
##             main   #45235       +/-   ##
===========================================
- Coverage   89.85%   68.66%   -21.19%     
===========================================
  Files        6249     6093      -156     
  Lines      398125   369547    -28578     
  Branches    80223    78263     -1960     
===========================================
- Hits       357739   253758   -103981     
- Misses      34076   112977    +78901     
+ Partials     6310     2812     -3498     
Flag Coverage Δ
api 51.94% <100.00%> (-37.78%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Eapp1e
Eapp1e force-pushed the fix/app-runner-sys-timestamp branch 2 times, most recently from 273053c to 3413b07 Compare November 1, 2026 09:54
@github-actions github-actions Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Nov 1, 2026
… workflow app runner

`WorkflowAppRunner.prepare()` builds the START node system variables with
`timestamp=int(naive_utc_now().timestamp())`. That value is a naive UTC wall clock,
and `datetime.timestamp()` interprets naive values in the process-local timezone, so
every workflow run started from the app runner reports an epoch that is shifted by the
host's UTC offset (`sys.timestamp` is 8 hours behind on a `TZ=Asia/Shanghai` host).

`_setup_variable_pool()` in `services/workflow_service.py` had the same call and was
fixed the same way, so this brings the second START node bootstrap path in line with
the `to_utc_timestamp()` contract in `libs/datetime_utils.py`.

Signed-off-by: Eapp1e <163811520+Eapp1e@users.noreply.github.com>
@Eapp1e
Eapp1e force-pushed the fix/app-runner-sys-timestamp branch from 3413b07 to 0dd4b6d Compare November 1, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] API response timestamps are offset by the host timezone when TZ != UTC (naive UTC datetimes serialized with .timestamp())

1 participant