Billionmail/BillionMail · warning

start time must be greater or equal to end time

Error message

start time must be greater or equal to end time

What it means

FailedList validates the requested time window and defaults EndTime to now when zero. If EndTime is earlier than StartTime, the query would return nonsense, so the handler rejects it with this error. Despite the wording, the requirement is end >= start.

Source

Thrown at core/internal/controller/overview/overview_v1_failed_list.go:22

	"billionmail-core/internal/service/maillog_stat"
	"billionmail-core/internal/service/public"
	"context"
	"fmt"
	"github.com/gogf/gf/v2/util/gconv"
	"time"

	"billionmail-core/api/overview/v1"
)

func (c *ControllerV1) FailedList(ctx context.Context, req *v1.FailedListReq) (res *v1.FailedListRes, err error) {
	res = &v1.FailedListRes{}

	if req.EndTime == 0 {
		req.EndTime = time.Now().Unix()
	}

	if req.EndTime < req.StartTime {
		err = fmt.Errorf("start time must be greater or equal to end time")
		return
	}

	overview := maillog_stat.NewOverview()
	failedList := overview.FailedList(req.CampaignId, req.Domain, req.StartTime, req.EndTime)

	err = gconv.Struct(failedList, &res.Data)

	if err != nil {
		err = fmt.Errorf("failed to convert overview data: %v", err)
		return
	}

	res.SetSuccess(public.LangCtx(ctx, "Success"))

	return
}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Ensure StartTime <= EndTime in Unix seconds before calling the API
  2. Convert timestamps to seconds (divide ms by 1000) if sending millisecond precision
  3. Check NTP/clock sync if times come from a skewed machine
  4. Swap the values if they were accidentally reversed

Example fix

// before
req.StartTime = time.Now().Add(24 * time.Hour).Unix()
req.EndTime = time.Now().Unix()
// after
req.EndTime = time.Now().Unix()
req.StartTime = req.EndTime - 24*3600
Defensive patterns

Strategy: validation

Validate before calling

if (typeof startTime !== 'number' || typeof endTime !== 'number') throw new Error('timestamps must be Unix seconds');
if (startTime > endTime) throw new Error('start time must be <= end time');

Type guard

function isUnixSeconds(n: unknown): n is number {
  return typeof n === 'number' && Number.isInteger(n) && n < 1e11; // excludes ms timestamps
}

Try / catch

try {
  const data = await api.failedList({ start_time: s, end_time: e });
} catch (err) {
  if (String(err.message).includes('greater or equal to end time')) {
    [s, e] = [Math.min(s, e), Math.max(s, e)]; // or fix units
  }
}

Prevention

When it happens

Trigger: Calling the failed-list API with StartTime greater than EndTime, e.g. start=now, end=yesterday; forgetting that EndTime defaults to now while passing a StartTime in the future (e.g. wrong unit — milliseconds instead of seconds); clock-skewed clients sending future start times.

Common situations: Client sends timestamps in milliseconds (13-digit) while server expects Unix seconds, making start appear far in the future; reversed arguments when constructing the request; dashboard 'last 24h' logic computing start after an end timestamp captured earlier.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/3ad2fac75c5b2150. Report an issue: GitHub.