HKUDS/DeepTutor · error · HTTPException

day must be YYYY-MM-DD

Error message

day must be YYYY-MM-DD

What it means

HTTP 400 raised by DELETE /trace/{surface}/day/{day} in deeptutor/api/routers/memory.py:714 when the day path parameter fails date.fromisoformat parsing. Only strict YYYY-MM-DD strings are accepted so the trace files for exactly one day can be selected and deleted.

Solutions

  1. Format the date as an ISO string before the request: in JS use date.toISOString().slice(0,10); in Python use date.isoformat()
  2. Validate the day segment with a ^\d{4}-\d{2}-\d{2}$ regex (plus a real calendar check) before calling
  3. Add a shared date-formatting util so all call sites emit ISO dates

Example fix

// before
const day = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
// after
const day = d.toISOString().slice(0, 10); // '2026-08-27'
Defensive patterns

Strategy: validation

Validate before calling

function isoDay(d: Date): string { return d.toISOString().slice(0, 10); }
if (!/^\d{4}-\d{2}-\d{2}$/.test(day)) throw new Error('bad date');

Type guard

function isIsoDay(s: string): boolean { return /^\d{4}-\d{2}-\d{2}$/.test(s) && !isNaN(Date.parse(s)); }

Try / catch

try { await clearTraceDay(surface, day); } catch (e) { if (e.status === 400) throw new Error(`invalid day format: ${day}`); else throw e; }

Prevention

When it happens

Trigger: Passing dates like '2026-8-7' (no zero padding), '20260807', '07-08-2026', or any non-date string in the day segment.

Common situations: Client-side date formatting that omits zero-padding (common with JS getMonth()/getDate()), locale-formatted dates, or Unix timestamps passed instead of ISO dates.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/548af6abb1c4e99a. Report an issue: GitHub.

Appendix: source

Thrown at deeptutor/api/routers/memory.py:714

async def clear_trace(surface: str):
    surf = _validate_surface(surface)
    removed = 0
    for path in paths.trace_dir(surf).glob("*.jsonl"):
        try:
            path.unlink()
            removed += 1
        except OSError:
            continue
    return {"surface": surf, "removed_files": removed}


@router.delete("/trace/{surface}/day/{day}")
async def clear_trace_day(surface: str, day: str):
    surf = _validate_surface(surface)
    try:
        parsed = date_cls.fromisoformat(day)
    except ValueError:
        raise HTTPException(status_code=400, detail="day must be YYYY-MM-DD")
    path = paths.trace_file(surf, parsed)
    if not path.exists():
        raise HTTPException(status_code=404, detail="no trace for that day")
    try:
        path.unlink()
    except OSError as exc:
        raise HTTPException(status_code=500, detail=str(exc))
    return {"surface": surf, "day": day, "deleted": True}


# ── Snapshot (L1 workspace mirror) ───────────────────────────────────────


@router.get("/snapshot/{surface}")
async def get_snapshot(surface: str):
    """Return the current entity list for ``surface`` from workspace.

    Snapshot is always derived live from workspace at call time. The response

View on GitHub (pinned to 3e82f13042)