From ad274a78428b6e3cdd7e483ff29c3e2d8af143b1 Mon Sep 17 00:00:00 2001 From: Evo Date: Tue, 23 Jun 2026 01:04:27 +0800 Subject: [PATCH 1/2] fix(http): reject negative limit/offset on list endpoints with 422 instead of 500 Several user-facing GET list endpoints declared limit/offset without ge constraints, so a negative value flowed straight into Postgres LIMIT/OFFSET (emitted with no max(0, ...) clamp), which raises 'LIMIT/OFFSET must not be negative'. The generic `except Exception -> HTTPException(500, str(e))` then turned a client input error into a 500 that also leaked the raw Postgres error string. Add Query(ge=...) constraints (limit ge=0, offset ge=0) on the affected endpoints (graph, memories/list, documents, tags, entities, entities/graph), matching the ge constraints already enforced on the sibling list endpoints (document-chunks, directives, async-ops, audit) so FastAPI returns a clean 422 at the boundary. ge=0 rejects only negatives and preserves limit=0 (a valid empty page), so there is no behavior change for any previously-valid request. --- hindsight-api-slim/hindsight_api/api/http.py | 20 ++--- ...est_list_endpoint_pagination_validation.py | 82 +++++++++++++++++++ 2 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py diff --git a/hindsight-api-slim/hindsight_api/api/http.py b/hindsight-api-slim/hindsight_api/api/http.py index 705f50aff..3e0d06884 100644 --- a/hindsight-api-slim/hindsight_api/api/http.py +++ b/hindsight-api-slim/hindsight_api/api/http.py @@ -3465,7 +3465,7 @@ async def metrics_endpoint(): async def api_graph( bank_id: str, type: str | None = None, - limit: int = 1000, + limit: int = Query(default=1000, ge=0), q: str | None = None, tags: list[str] | None = Query(None), tags_match: str = "all_strict", @@ -3513,8 +3513,8 @@ async def api_list( consolidation_state: str | None = None, state: str | None = None, document_id: str | None = None, - limit: int = 100, - offset: int = 0, + limit: int = Query(default=100, ge=0), + offset: int = Query(default=0, ge=0), request_context: RequestContext = Depends(get_request_context), ): """ @@ -4248,8 +4248,8 @@ async def api_memories_timeseries( ) async def api_list_entities( bank_id: str, - limit: int = Query(default=100, description="Maximum number of entities to return"), - offset: int = Query(default=0, description="Offset for pagination"), + limit: int = Query(default=100, ge=0, description="Maximum number of entities to return"), + offset: int = Query(default=0, ge=0, description="Offset for pagination"), request_context: RequestContext = Depends(get_request_context), ): """List entities for a memory bank with pagination.""" @@ -4284,7 +4284,7 @@ async def api_list_entities( ) async def api_entity_graph( bank_id: str, - limit: int = Query(default=1000, description="Maximum number of co-occurrence edges to return"), + limit: int = Query(default=1000, ge=0, description="Maximum number of co-occurrence edges to return"), min_count: int = Query(default=1, description="Minimum cooccurrence_count to include an edge"), request_context: RequestContext = Depends(get_request_context), ): @@ -4911,8 +4911,8 @@ async def api_list_documents( tags_match: str = Query( "any_strict", description="How to match tags: 'any', 'all', 'any_strict', 'all_strict'" ), - limit: int = 100, - offset: int = 0, + limit: int = Query(default=100, ge=0), + offset: int = Query(default=0, ge=0), request_context: RequestContext = Depends(get_request_context), ): """ @@ -5096,8 +5096,8 @@ async def api_list_tags( default="memories", description="Where to read tags from: 'memories' (memory_units, default) or 'mental_models'.", ), - limit: int = Query(default=100, description="Maximum number of tags to return"), - offset: int = Query(default=0, description="Offset for pagination"), + limit: int = Query(default=100, ge=0, description="Maximum number of tags to return"), + offset: int = Query(default=0, ge=0, description="Offset for pagination"), request_context: RequestContext = Depends(get_request_context), ): """ diff --git a/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py b/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py new file mode 100644 index 000000000..acc67a1c2 --- /dev/null +++ b/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py @@ -0,0 +1,82 @@ +"""Regression: user-facing GET list endpoints must reject negative limit/offset +with a clean 422 at the FastAPI boundary instead of letting the value reach +Postgres (``LIMIT/OFFSET must not be negative``) and surfacing as an opaque 500 +that also leaks the raw Postgres error string. + +This makes pagination validation consistent with the sibling list endpoints in +the same router (document-chunks / directives / async-ops / audit) that already +declare ``Query(..., ge=...)``. The engine emits ``LIMIT $n OFFSET $n`` with no +``max(0, ...)`` clamp, so the guard has to live at the request boundary. +""" + +import uuid + +import httpx +import pytest +import pytest_asyncio + +from hindsight_api import RequestContext +from hindsight_api.api import create_app + + +@pytest_asyncio.fixture +async def api_client(memory): + app = create_app(memory, initialize_memory=False) + transport = httpx.ASGITransport(app=app) + async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: + yield client + + +def _url(bank_id: str, suffix: str) -> str: + return f"/v1/default/banks/{bank_id}/{suffix}" + + +# Endpoints that accept a ``limit`` query param. +LIMIT_ENDPOINTS = [ + "graph", + "memories/list", + "entities", + "entities/graph", + "documents", + "tags", +] + +# Subset that also accept an ``offset`` query param. +OFFSET_ENDPOINTS = [ + "memories/list", + "entities", + "documents", + "tags", +] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("suffix", LIMIT_ENDPOINTS) +async def test_negative_limit_returns_422_not_500(api_client, suffix): + bank_id = f"pag-{uuid.uuid4().hex[:8]}" + resp = await api_client.get(_url(bank_id, suffix), params={"limit": -1}) + # FastAPI validation runs before the handler / DB, so a bad pagination input + # is a clean 422 — never a 500 leaking the raw Postgres error. + assert resp.status_code == 422, resp.text + + +@pytest.mark.asyncio +@pytest.mark.parametrize("suffix", OFFSET_ENDPOINTS) +async def test_negative_offset_returns_422_not_500(api_client, suffix): + bank_id = f"pag-{uuid.uuid4().hex[:8]}" + resp = await api_client.get(_url(bank_id, suffix), params={"offset": -1}) + assert resp.status_code == 422, resp.text + + +@pytest.mark.asyncio +@pytest.mark.parametrize("limit", [0, 1, 100]) +async def test_valid_limit_is_accepted_including_zero(api_client, memory, limit): + # Positive control: ge=0 rejects only NEGATIVE limits. A non-negative limit + # — including limit=0 (a valid empty page, LIMIT 0) — must still be accepted, + # so this fix does not change behavior for any previously-valid input. + bank_id = f"pag-{uuid.uuid4().hex[:8]}" + await memory.get_bank_profile(bank_id=bank_id, request_context=RequestContext()) + resp = await api_client.get( + _url(bank_id, "memories/list"), params={"limit": limit, "offset": 0} + ) + assert resp.status_code == 200, resp.text From 24ded28ed9a140b3b1ae21c20168345ab4504c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Tue, 23 Jun 2026 11:21:19 +0200 Subject: [PATCH 2/2] chore: regenerate OpenAPI spec and clients for ge=0 pagination constraints Adds minimum:0 to limit/offset params across openapi.json, docs-skill spec, Go openapi.yaml, and Python clients; lint reformats the new test. --- ...est_list_endpoint_pagination_validation.py | 4 +-- hindsight-clients/go/api/openapi.yaml | 10 ++++++ .../hindsight_client_api/api/documents_api.py | 14 ++++---- .../hindsight_client_api/api/entities_api.py | 18 +++++------ .../hindsight_client_api/api/memory_api.py | 32 +++++++++---------- hindsight-docs/static/openapi.json | 10 ++++++ skills/hindsight-docs/references/openapi.json | 10 ++++++ 7 files changed, 63 insertions(+), 35 deletions(-) diff --git a/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py b/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py index acc67a1c2..7610542c0 100644 --- a/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py +++ b/hindsight-api-slim/tests/test_list_endpoint_pagination_validation.py @@ -76,7 +76,5 @@ async def test_valid_limit_is_accepted_including_zero(api_client, memory, limit) # so this fix does not change behavior for any previously-valid input. bank_id = f"pag-{uuid.uuid4().hex[:8]}" await memory.get_bank_profile(bank_id=bank_id, request_context=RequestContext()) - resp = await api_client.get( - _url(bank_id, "memories/list"), params={"limit": limit, "offset": 0} - ) + resp = await api_client.get(_url(bank_id, "memories/list"), params={"limit": limit, "offset": 0}) assert resp.status_code == 200, resp.text diff --git a/hindsight-clients/go/api/openapi.yaml b/hindsight-clients/go/api/openapi.yaml index 2e8b9d5f2..a0ddf6677 100644 --- a/hindsight-clients/go/api/openapi.yaml +++ b/hindsight-clients/go/api/openapi.yaml @@ -80,6 +80,7 @@ paths: required: false schema: default: 1000 + minimum: 0 title: Limit type: integer style: form @@ -212,6 +213,7 @@ paths: required: false schema: default: 100 + minimum: 0 title: Limit type: integer style: form @@ -221,6 +223,7 @@ paths: required: false schema: default: 0 + minimum: 0 title: Offset type: integer style: form @@ -732,6 +735,7 @@ paths: schema: default: 100 description: Maximum number of entities to return + minimum: 0 title: Limit type: integer style: form @@ -743,6 +747,7 @@ paths: schema: default: 0 description: Offset for pagination + minimum: 0 title: Offset type: integer style: form @@ -792,6 +797,7 @@ paths: schema: default: 1000 description: Maximum number of co-occurrence edges to return + minimum: 0 title: Limit type: integer style: form @@ -1690,6 +1696,7 @@ paths: required: false schema: default: 100 + minimum: 0 title: Limit type: integer style: form @@ -1699,6 +1706,7 @@ paths: required: false schema: default: 0 + minimum: 0 title: Offset type: integer style: form @@ -2046,6 +2054,7 @@ paths: schema: default: 100 description: Maximum number of tags to return + minimum: 0 title: Limit type: integer style: form @@ -2057,6 +2066,7 @@ paths: schema: default: 0 description: Offset for pagination + minimum: 0 title: Offset type: integer style: form diff --git a/hindsight-clients/python/hindsight_client_api/api/documents_api.py b/hindsight-clients/python/hindsight_client_api/api/documents_api.py index c82983f72..3fa44fb50 100644 --- a/hindsight-clients/python/hindsight_client_api/api/documents_api.py +++ b/hindsight-clients/python/hindsight_client_api/api/documents_api.py @@ -16,7 +16,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union from typing_extensions import Annotated -from pydantic import Field, StrictInt, StrictStr +from pydantic import Field, StrictStr from typing import List, Optional from typing_extensions import Annotated from hindsight_client_api.models.chunk_response import ChunkResponse @@ -1244,8 +1244,8 @@ async def list_documents( q: Annotated[Optional[StrictStr], Field(description="Case-insensitive substring filter on document ID (e.g. 'report' matches 'report-2024')")] = None, tags: Annotated[Optional[List[StrictStr]], Field(description="Filter documents by tags")] = None, tags_match: Annotated[Optional[StrictStr], Field(description="How to match tags: 'any', 'all', 'any_strict', 'all_strict'")] = None, - limit: Optional[StrictInt] = None, - offset: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, + offset: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -1336,8 +1336,8 @@ async def list_documents_with_http_info( q: Annotated[Optional[StrictStr], Field(description="Case-insensitive substring filter on document ID (e.g. 'report' matches 'report-2024')")] = None, tags: Annotated[Optional[List[StrictStr]], Field(description="Filter documents by tags")] = None, tags_match: Annotated[Optional[StrictStr], Field(description="How to match tags: 'any', 'all', 'any_strict', 'all_strict'")] = None, - limit: Optional[StrictInt] = None, - offset: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, + offset: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -1428,8 +1428,8 @@ async def list_documents_without_preload_content( q: Annotated[Optional[StrictStr], Field(description="Case-insensitive substring filter on document ID (e.g. 'report' matches 'report-2024')")] = None, tags: Annotated[Optional[List[StrictStr]], Field(description="Filter documents by tags")] = None, tags_match: Annotated[Optional[StrictStr], Field(description="How to match tags: 'any', 'all', 'any_strict', 'all_strict'")] = None, - limit: Optional[StrictInt] = None, - offset: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, + offset: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, diff --git a/hindsight-clients/python/hindsight_client_api/api/entities_api.py b/hindsight-clients/python/hindsight_client_api/api/entities_api.py index 4e1eaa444..ef357b0d4 100644 --- a/hindsight-clients/python/hindsight_client_api/api/entities_api.py +++ b/hindsight-clients/python/hindsight_client_api/api/entities_api.py @@ -338,7 +338,7 @@ def _get_entity_serialize( async def get_entity_graph( self, bank_id: StrictStr, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of co-occurrence edges to return")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of co-occurrence edges to return")] = None, min_count: Annotated[Optional[StrictInt], Field(description="Minimum cooccurrence_count to include an edge")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ @@ -418,7 +418,7 @@ async def get_entity_graph( async def get_entity_graph_with_http_info( self, bank_id: StrictStr, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of co-occurrence edges to return")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of co-occurrence edges to return")] = None, min_count: Annotated[Optional[StrictInt], Field(description="Minimum cooccurrence_count to include an edge")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ @@ -498,7 +498,7 @@ async def get_entity_graph_with_http_info( async def get_entity_graph_without_preload_content( self, bank_id: StrictStr, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of co-occurrence edges to return")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of co-occurrence edges to return")] = None, min_count: Annotated[Optional[StrictInt], Field(description="Minimum cooccurrence_count to include an edge")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ @@ -650,8 +650,8 @@ def _get_entity_graph_serialize( async def list_entities( self, bank_id: StrictStr, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None, - offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of entities to return")] = None, + offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Offset for pagination")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -730,8 +730,8 @@ async def list_entities( async def list_entities_with_http_info( self, bank_id: StrictStr, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None, - offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of entities to return")] = None, + offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Offset for pagination")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -810,8 +810,8 @@ async def list_entities_with_http_info( async def list_entities_without_preload_content( self, bank_id: StrictStr, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None, - offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of entities to return")] = None, + offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Offset for pagination")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, diff --git a/hindsight-clients/python/hindsight_client_api/api/memory_api.py b/hindsight-clients/python/hindsight_client_api/api/memory_api.py index 88e4bcd12..cfbdfee40 100644 --- a/hindsight-clients/python/hindsight_client_api/api/memory_api.py +++ b/hindsight-clients/python/hindsight_client_api/api/memory_api.py @@ -16,7 +16,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union from typing_extensions import Annotated -from pydantic import Field, StrictInt, StrictStr, field_validator +from pydantic import Field, StrictStr, field_validator from typing import Any, List, Optional from typing_extensions import Annotated from hindsight_client_api.models.clear_memory_observations_response import ClearMemoryObservationsResponse @@ -952,7 +952,7 @@ async def get_graph( self, bank_id: StrictStr, type: Optional[StrictStr] = None, - limit: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, q: Optional[StrictStr] = None, tags: Optional[List[Optional[StrictStr]]] = None, tags_match: Optional[StrictStr] = None, @@ -1052,7 +1052,7 @@ async def get_graph_with_http_info( self, bank_id: StrictStr, type: Optional[StrictStr] = None, - limit: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, q: Optional[StrictStr] = None, tags: Optional[List[Optional[StrictStr]]] = None, tags_match: Optional[StrictStr] = None, @@ -1152,7 +1152,7 @@ async def get_graph_without_preload_content( self, bank_id: StrictStr, type: Optional[StrictStr] = None, - limit: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, q: Optional[StrictStr] = None, tags: Optional[List[Optional[StrictStr]]] = None, tags_match: Optional[StrictStr] = None, @@ -1940,8 +1940,8 @@ async def list_memories( consolidation_state: Optional[StrictStr] = None, state: Optional[StrictStr] = None, document_id: Optional[StrictStr] = None, - limit: Optional[StrictInt] = None, - offset: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, + offset: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -2040,8 +2040,8 @@ async def list_memories_with_http_info( consolidation_state: Optional[StrictStr] = None, state: Optional[StrictStr] = None, document_id: Optional[StrictStr] = None, - limit: Optional[StrictInt] = None, - offset: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, + offset: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -2140,8 +2140,8 @@ async def list_memories_without_preload_content( consolidation_state: Optional[StrictStr] = None, state: Optional[StrictStr] = None, document_id: Optional[StrictStr] = None, - limit: Optional[StrictInt] = None, - offset: Optional[StrictInt] = None, + limit: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, + offset: Optional[Annotated[int, Field(strict=True, ge=0)]] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -2612,8 +2612,8 @@ async def list_tags( bank_id: StrictStr, q: Annotated[Optional[StrictStr], Field(description="Wildcard pattern to filter tags (e.g., 'user:*' for user:alice, '*-admin' for role-admin). Use '*' as wildcard. Case-insensitive.")] = None, source: Annotated[Optional[StrictStr], Field(description="Where to read tags from: 'memories' (memory_units, default) or 'mental_models'.")] = None, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of tags to return")] = None, - offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of tags to return")] = None, + offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Offset for pagination")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -2700,8 +2700,8 @@ async def list_tags_with_http_info( bank_id: StrictStr, q: Annotated[Optional[StrictStr], Field(description="Wildcard pattern to filter tags (e.g., 'user:*' for user:alice, '*-admin' for role-admin). Use '*' as wildcard. Case-insensitive.")] = None, source: Annotated[Optional[StrictStr], Field(description="Where to read tags from: 'memories' (memory_units, default) or 'mental_models'.")] = None, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of tags to return")] = None, - offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of tags to return")] = None, + offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Offset for pagination")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, @@ -2788,8 +2788,8 @@ async def list_tags_without_preload_content( bank_id: StrictStr, q: Annotated[Optional[StrictStr], Field(description="Wildcard pattern to filter tags (e.g., 'user:*' for user:alice, '*-admin' for role-admin). Use '*' as wildcard. Case-insensitive.")] = None, source: Annotated[Optional[StrictStr], Field(description="Where to read tags from: 'memories' (memory_units, default) or 'mental_models'.")] = None, - limit: Annotated[Optional[StrictInt], Field(description="Maximum number of tags to return")] = None, - offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None, + limit: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Maximum number of tags to return")] = None, + offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Offset for pagination")] = None, authorization: Optional[StrictStr] = None, _request_timeout: Union[ None, diff --git a/hindsight-docs/static/openapi.json b/hindsight-docs/static/openapi.json index 4b06c5c53..0333c2194 100644 --- a/hindsight-docs/static/openapi.json +++ b/hindsight-docs/static/openapi.json @@ -115,6 +115,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 1000, "title": "Limit" } @@ -341,6 +342,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 100, "title": "Limit" } @@ -351,6 +353,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 0, "title": "Offset" } @@ -1082,6 +1085,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Maximum number of entities to return", "default": 100, "title": "Limit" @@ -1094,6 +1098,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Offset for pagination", "default": 0, "title": "Offset" @@ -1165,6 +1170,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Maximum number of co-occurrence edges to return", "default": 1000, "title": "Limit" @@ -2474,6 +2480,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 100, "title": "Limit" } @@ -2484,6 +2491,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 0, "title": "Offset" } @@ -2960,6 +2968,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Maximum number of tags to return", "default": 100, "title": "Limit" @@ -2972,6 +2981,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Offset for pagination", "default": 0, "title": "Offset" diff --git a/skills/hindsight-docs/references/openapi.json b/skills/hindsight-docs/references/openapi.json index 4b06c5c53..0333c2194 100644 --- a/skills/hindsight-docs/references/openapi.json +++ b/skills/hindsight-docs/references/openapi.json @@ -115,6 +115,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 1000, "title": "Limit" } @@ -341,6 +342,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 100, "title": "Limit" } @@ -351,6 +353,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 0, "title": "Offset" } @@ -1082,6 +1085,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Maximum number of entities to return", "default": 100, "title": "Limit" @@ -1094,6 +1098,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Offset for pagination", "default": 0, "title": "Offset" @@ -1165,6 +1170,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Maximum number of co-occurrence edges to return", "default": 1000, "title": "Limit" @@ -2474,6 +2480,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 100, "title": "Limit" } @@ -2484,6 +2491,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "default": 0, "title": "Offset" } @@ -2960,6 +2968,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Maximum number of tags to return", "default": 100, "title": "Limit" @@ -2972,6 +2981,7 @@ "required": false, "schema": { "type": "integer", + "minimum": 0, "description": "Offset for pagination", "default": 0, "title": "Offset"