sansan0/TrendRadar · error · ValueError

period '{period_key}' 缺少 start 或 end 字段

Error message

period '{period_key}' 缺少 start 或 end 字段

What it means

Raised by _validate_timeline when a period's start and end are identical. A zero-length period is rejected because the scheduler's range semantics (start inclusive, end exclusive) would make it never match, hiding a likely config mistake. Format validity of each field is checked separately just before this comparison.

Source

Thrown at trendradar/core/scheduler.py:348

        # day_plan 引用完整性
        for day, plan_key in timeline["week_map"].items():
            if plan_key not in timeline["day_plans"]:
                raise ValueError(
                    f"week_map[{day}] 引用了不存在的 day_plan: {plan_key}"
                )

        # period 引用完整性
        for plan_key, plan in timeline["day_plans"].items():
            for period_key in plan.get("periods", []):
                if period_key not in timeline["periods"]:
                    raise ValueError(
                        f"day_plan[{plan_key}] 引用了不存在的 period: {period_key}"
                    )

        # 时间格式校验
        for period_key, period in timeline["periods"].items():
            if "start" not in period or "end" not in period:
                raise ValueError(
                    f"period '{period_key}' 缺少 start 或 end 字段"
                )
            self._validate_hhmm(period["start"], f"{period_key}.start")
            self._validate_hhmm(period["end"], f"{period_key}.end")
            if period["start"] == period["end"]:
                raise ValueError(
                    f"period '{period_key}' 的 start 与 end 不能相同: {period['start']}"
                )

        # 检查冲突策略下的重叠
        policy = timeline.get("overlap", {}).get("policy", "error_on_overlap")
        if policy == "error_on_overlap":
            self._check_period_overlaps(timeline)

    def _check_period_overlaps(self, timeline: Dict[str, Any]) -> None:
        """
        检查每个日计划中的时间段是否存在重叠

View on GitHub (pinned to 8ee26026ba)

Solutions

  1. Set end strictly after start (or before it for a cross-midnight span), e.g. "12:00"-"12:01" for a near-instant window.
  2. If you meant 'all day', use "00:00"-"23:59" or the default config instead.
  3. Double-check copy-pasted periods where end was meant to be edited.

Example fix

# before
periods:
  ping: {start: "12:00", end: "12:00"}
# after
periods:
  ping: {start: "12:00", end: "12:05"}
Defensive patterns

Strategy: validation

Validate before calling

for key, p in timeline.get("periods", {}).items():
    if p["start"] == p["end"]:
        raise ValueError(f"period '{key}': start == end ({p['start']}); use a real span")

Type guard

def is_nonzero_period(p: dict) -> bool:
    return p.get("start") != p.get("end")

Try / catch

try:
    scheduler = Scheduler(schedule_config, timeline_data)
except ValueError as e:
    if "start 与 end 不能相同" in str(e):
        raise SystemExit(f"timeline config error: {e}")
    raise

Prevention

When it happens

Trigger: periods: {noon: {start: "12:00", end: "12:00"}} — intended as a one-shot instant but encoded as equal boundaries; copy-paste where end was not updated after start.

Common situations: Attempting to schedule a single-moment trigger; placeholder values left in both fields; timezone shifts applied equally to both fields after copy-paste.

Related errors


AI-assisted analysis of sansan0/TrendRadar@8ee26026ba (2026-08-15). Data as JSON: /api/errors/4eb7d58493db004f. Report an issue: GitHub.