← Community
bugfixed

max_watermark_jump_seconds and seen_window_seconds skip contract validation — a string value installs fine, then TypeErrors on a scheduled t

ShwetaShweta#119d ago · 141 views
affected: station-v0.64fixed in: station-v0.66

Reproduction steps:

  1. In a module manifest, declare an incremental command with a non-integer jump limit:

"incremental": {
"since_param": "since",
"items_field": "changes",
"cursor_field": "change_ref",
"watermark_from": "modified_at",
"watermark_type": "timestamp",
"max_watermark_jump_seconds": "30d"
}

  1. Sign and install the module, then start the Studio.
  2. Observe it loads cleanly: "loaded=4 rejected=0".
  3. Call watermark_store.check_advance() with that contract and a candidate beyond the current mark.

Expected: the contract is rejected at install or lint time. incremental_contract's own docstring says exactly this — "Raises ContractError on anything malformed. Callers surface that at install time (spec §9): a contract that is wrong must fail loudly while a human is watching, not silently skip customers on a 3am tick."

Actual: parse() accepts it, the module installs, and the failure lands at runtime on a scheduled tick:

TypeError: '>' not supported between instances of 'float' and 'str'

Root cause: incremental_contract.parse() validates lookback_seconds but reads the other two straight through with .get():

lookback = inc.get("lookback_seconds", DEFAULT_LOOKBACK_SECONDS)
if isinstance(lookback, bool) or not isinstance(lookback, int) or lookback < 0:
raise ContractError(...) # validated

"seen_window_seconds": inc.get("seen_window_seconds", DEFAULT_SEEN_WINDOW_SECONDS),
"max_watermark_jump_seconds": inc.get("max_watermark_jump_seconds",
DEFAULT_MAX_WATERMARK_JUMP_SECONDS),
# neither type-checked

watermark_store.check_advance() then compares it directly:

if (n - c).total_seconds() > limit:

Two more values that parse but misbehave:

  • max_watermark_jump_seconds: 0 is accepted and refuses EVERY forward advance

("forward jump of 1s exceeds 0s"). The schedule keeps running and the mark
never moves — a permanent silent stall rather than an error.

  • seen_window_seconds: -1 is accepted.

Suggested fix: run both through the same _positive_int() helper that lookback_seconds uses, with a floor of 1 for max_watermark_jump_seconds so 0 cannot mean "never advance".

Verified: parse() accepting the string, and the resulting TypeError from check_advance(), reproduced against the v0.65 source. Install acceptance confirmed live on my own station at v0.64 — the module loaded with the string value in place and no warning.

Station version (railcall version): station-v0.64 (source checked against station-v0.65)
Module slug + version: shweta/zoho-crm v0.8.0

3 pts

1 reply

Confirmed and reproduced against incremental_contract.pylookback_seconds is int-validated (lines 177-179), but seen_window_seconds and max_watermark_jump_seconds are read straight through with .get() and never type-checked (195-198). A string like "30d" installs clean, then check_advance() TypeErrors on a scheduled tick — exactly the "3am silent skip" the contract docstring promises to prevent. Credited 3 pts (moderate). Fix incoming: validating all three ints at parse time so it fails loudly at install.

Sign in to reply.