{"record":{"id":"33a936c30f041256","repo":"MemPalace/mempalace","slug":"limit-must-be-a-positive-integer","errorCode":null,"errorMessage":"limit must be a positive integer","messagePattern":"limit must be a positive integer","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/logstream.py","lineNumber":712,"sourceCode":"          event in append order (rowid), regardless of timestamp ties.\n        - ``since_created_at`` is inclusive (``>=``) so second-granularity\n          timestamps never skip events; callers dedup by ``id``.\n        - ``to_agent`` also matches broadcast events (``to_agent='*'``).\n        \"\"\"\n        stream = _sanitize_routing(stream, \"stream\", required=False)\n        room = _sanitize_routing(room, \"room\", required=False)\n        if type not in (None, \"\"):\n            type = _sanitize_event_type(type)\n        else:\n            type = None\n        to_agent = _sanitize_routing(to_agent, \"to_agent\", required=False)\n        from_agent = _sanitize_routing(from_agent, \"from_agent\", required=False)\n        correlation_id = _sanitize_routing(correlation_id, \"correlation_id\", required=False)\n        status = _sanitize_status(status)\n        since_event_id = _sanitize_routing(since_event_id, \"since_event_id\", required=False)\n        since_created_at = sanitize_iso_temporal(since_created_at, \"since_created_at\") or None\n        if not isinstance(limit, int) or limit < 1:\n            raise ValueError(\"limit must be a positive integer\")\n        limit = min(limit, MAX_LIST_LIMIT)\n\n        where = []\n        params = []\n        for column, value in (\n            (\"stream\", stream),\n            (\"room\", room),\n            (\"type\", type),\n            (\"from_agent\", from_agent),\n            (\"correlation_id\", correlation_id),\n            (\"status\", status),\n        ):\n            if value is not None:\n                where.append(f\"{column} = ?\")\n                params.append(value)\n        if to_agent is not None:\n            where.append(\"(to_agent = ? OR to_agent = '*')\")\n            params.append(to_agent)","sourceCodeStart":694,"sourceCodeEnd":730,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/logstream.py#L694-L730","documentation":"list_events (and wait_events, which forwards its filters) requires `limit` to be an int >= 1. Zero, negative numbers, floats like 10.0, and None all fail. Valid limits are then clamped to MAX_LIST_LIMIT = 500, so any positive integer works but is capped. bool is an int subclass, so True passes as limit=1 — usually a latent bug rather than intent.","triggerScenarios":"list_events(limit=0); limit=-1 to mean 'all'; limit=None when no default applied; limit=50.0 from a config float; limit computed as len(items) when the list is empty.","commonSituations":"Config files parsed with float values; callers using 0 as a sentinel for 'no limit'; a computed limit that can be 0 for empty result sets; JSON input where the field is optional and arrives as null.","solutions":["Pass a positive int: limit=50 (the DEFAULT_LIST_LIMIT) or anything 1..500.","For 'as much as possible' use limit=500 (MAX_LIST_LIMIT) and paginate with since_event_id.","Guard computed values: limit=max(1, min(int(limit or 50), 500))."],"exampleFix":"// before\nevts = ls.list_events(stream=\"project/x\", limit=len(rooms))  # 0 when empty\n// after\nlimit = max(1, min(len(rooms) or 50, 500))\nevts = ls.list_events(stream=\"project/x\", limit=limit)","handlingStrategy":"validation","validationCode":"def safe_limit(limit, default=50, cap=500):\n    if limit is None:\n        return default\n    return max(1, min(int(limit), cap))\n\nlimit = safe_limit(raw_limit)","typeGuard":"def is_valid_limit(v) -> bool:\n    return isinstance(v, int) and not isinstance(v, bool) and v >= 1","tryCatchPattern":"try:\n    evts = ls.list_events(stream=s, limit=limit)\nexcept ValueError as e:\n    if \"limit must be a positive integer\" in str(e):\n        evts = ls.list_events(stream=s, limit=50)\n    else:\n        raise","preventionTips":["Never use 0 or -1 as 'all' — cap at MAX_LIST_LIMIT (500) and paginate with since_event_id.","Coerce config floats to int at load time.","Exclude bool from limit handling; it silently passes as 1."],"tags":["validation","logstream","pagination","limit"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}