Skip to content

ValidationError: 1 validation error for RecDotGovCampsiteResponse campsites field required (type=value_error.missing) #415

Description

@jimjed

A ValidationError is raised when trying to fetch campsites for Recreation Area ID: '2845'.

Command used is as below:

camply campsites \
    --rec-area 2845 \
    --start-date 2026-06-19 \
    --end-date 2026-06-21 \
    --notifications silent

Output was as below:

[2026-06-11 16:51:19] CAMPLY   camply, the campsite finder ⛺️                                                                                                                                           
[2026-06-11 16:51:19] INFO     Using Camply Provider: "RecreationDotGov"                                                                                                                                
[2026-06-11 16:51:19] INFO     2 booking nights selected for search, ranging from 2026-06-19 to 2026-06-20                                                                                              
[2026-06-11 16:51:19] INFO     Retrieving Facility Information for Recreation Area ID: `2845`.                                                                                                          
[2026-06-11 16:51:20] INFO     8 Matching Campgrounds Found                                                                                                                                             
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, USA (#2845) - 🏕  Colonial Creek North Campground (#246855)                                                                              
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, USA (#2845) - 🏕  Colonial Creek South Campground (#255201)                                                                              
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, USA (#2845) - 🏕  Harlequin Campground (#10101324)                                                                                       
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, USA (#2845) - 🏕  Lower Goodell Group Campground (#234088)                                                                               
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, USA (#2845) - 🏕  Newhalem Creek Campground (#234060)                                                                                    
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, USA (#2845) - 🏕  Upper Goodell Group Campground (#234089)                                                                               
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, WA (#2845) - 🏕  Goodell Creek Campground (#246852)                                                                                      
[2026-06-11 16:51:20] INFO     ⛰  North Cascades National Park, WASHINGTON (#2845) - 🏕  Gorge Lake Campground (#10004932)                                                                               
[2026-06-11 16:51:20] INFO     Searching for campsites every 10 minutes.                                                                                                                                
[2026-06-11 16:51:20] INFO     Notifications active via: <SilentNotifications>                                                                                                                          
[2026-06-11 16:51:20] INFO     Only <SilentNotifications> enabled. I hope you're watching these logs.                                                                                                   
[2026-06-11 16:51:20] INFO     Searching across 8 campgrounds                                                                                                                                           
[2026-06-11 16:51:21] CAMPLY   Exiting camply 👋                                                                                                                                                        
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /Users/jimj/.local/bin/camply:6 in <module>                                                      │
│                                                                                                  │
│   3 from camply.cli import cli                                                                   │
│   4 if __name__ == '__main__':                                                                   │
│   5 │   sys.argv[0] = sys.argv[0].removesuffix('.exe')                                           │
│ ❱ 6 │   sys.exit(cli())                                                                          │
│   7                                                                                              │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/cli.py:883 in cli       │
│                                                                                                  │
│   880 │   Camply Command Line Utility Wrapper                                                    │
│   881 │   """                                                                                    │
│   882 │   try:                                                                                   │
│ ❱ 883 │   │   camply_command_line()                                                              │
│   884 │   except KeyboardInterrupt:                                                              │
│   885 │   │   logger.debug("Handling Exit Request")                                              │
│   886 │   finally:                                                                               │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/click/core.py:1161 in __call__ │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/rich_click/rich_group.py:21 in │
│ main                                                                                             │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/click/core.py:1082 in main     │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/click/core.py:1697 in invoke   │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/click/core.py:1443 in invoke   │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/click/core.py:788 in invoke    │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/click/decorators.py:45 in      │
│ new_func                                                                                         │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/cli.py:775 in campsites │
│                                                                                                  │
│   772 │   │   )                                                                                  │
│   773 │   provider_class: Type[BaseCampingSearch] = CAMPSITE_SEARCH_PROVIDER[provider]           │
│   774 │   camping_finder: BaseCampingSearch = provider_class(**provider_kwargs)                  │
│ ❱ 775 │   camping_finder.get_matching_campsites(**search_kwargs)                                 │
│   776                                                                                            │
│   777                                                                                            │
│   778 @camply_command_line.command(cls=RichCommand)                                              │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/search/base_search.py:5 │
│ 88 in get_matching_campsites                                                                     │
│                                                                                                  │
│    585 │   │   │   except Exception as e:                                                        │
│    586 │   │   │   │   if self.search_attempts >= 1:                                             │
│    587 │   │   │   │   │   self.notifier.last_gasp(error=e)                                      │
│ ❱  588 │   │   │   │   raise e                                                                   │
│    589 │   │   else:                                                                             │
│    590 │   │   │   starting_count = len(self.campsites_found)                                    │
│    591 │   │   │   matching_campsites = self._search_matching_campsites_available(               │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/search/base_search.py:5 │
│ 76 in get_matching_campsites                                                                     │
│                                                                                                  │
│    573 │   │   """                                                                               │
│    574 │   │   if continuous is True or search_once is True:                                     │
│    575 │   │   │   try:                                                                          │
│ ❱  576 │   │   │   │   self._search_campsites_continuous(                                        │
│    577 │   │   │   │   │   log=log,                                                              │
│    578 │   │   │   │   │   verbose=verbose,                                                      │
│    579 │   │   │   │   │   polling_interval=polling_interval,                                    │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/search/base_search.py:5 │
│ 10 in _search_campsites_continuous                                                               │
│                                                                                                  │
│    507 │   │   continuous_search_attempts = 1                                                    │
│    508 │   │   while continuous_search is True:                                                  │
│    509 │   │   │   starting_count = len(self.campsites_found)                                    │
│ ❱  510 │   │   │   self._continuous_search_retry(                                                │
│    511 │   │   │   │   log=log,                                                                  │
│    512 │   │   │   │   verbose=verbose,                                                          │
│    513 │   │   │   │   polling_interval=polling_interval,                                        │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/search/base_search.py:3 │
│ 56 in _continuous_search_retry                                                                   │
│                                                                                                  │
│    353 │   │   │   retry=tenacity.retry_if_exception_type(CampsiteNotFoundError),                │
│    354 │   │   │   wait=tenacity.wait.wait_fixed(int(polling_interval_minutes) * 60),            │
│    355 │   │   )                                                                                 │
│ ❱  356 │   │   matching_campsites = retryer.__call__(                                            │
│    357 │   │   │   fn=self._search_matching_campsites_available,                                 │
│    358 │   │   │   log=False,                                                                    │
│    359 │   │   │   verbose=False,                                                                │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/tenacity/__init__.py:379 in    │
│ __call__                                                                                         │
│                                                                                                  │
│   376 │   │                                                                                      │
│   377 │   │   retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)   │
│   378 │   │   while True:                                                                        │
│ ❱ 379 │   │   │   do = self.iter(retry_state=retry_state)                                        │
│   380 │   │   │   if isinstance(do, DoAttempt):                                                  │
│   381 │   │   │   │   try:                                                                       │
│   382 │   │   │   │   │   result = fn(*args, **kwargs)                                           │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/tenacity/__init__.py:314 in    │
│ iter                                                                                             │
│                                                                                                  │
│   311 │   │                                                                                      │
│   312 │   │   is_explicit_retry = fut.failed and isinstance(fut.exception(), TryAgain)           │
│   313 │   │   if not (is_explicit_retry or self.retry(retry_state)):                             │
│ ❱ 314 │   │   │   return fut.result()                                                            │
│   315 │   │                                                                                      │
│   316 │   │   if self.after is not None:                                                         │
│   317 │   │   │   self.after(retry_state)                                                        │
│                                                                                                  │
│ /opt/homebrew/Cellar/python@3.14/3.14.0_1/Frameworks/Python.framework/Versions/3.14/lib/python3. │
│ 14/concurrent/futures/_base.py:443 in result                                                     │
│                                                                                                  │
│   440 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:                     │
│   441 │   │   │   │   │   raise CancelledError()                                                 │
│   442 │   │   │   │   elif self._state == FINISHED:                                              │
│ ❱ 443 │   │   │   │   │   return self.__get_result()                                             │
│   444 │   │   │   │                                                                              │
│   445 │   │   │   │   self._condition.wait(timeout)                                              │
│   446                                                                                            │
│                                                                                                  │
│ /opt/homebrew/Cellar/python@3.14/3.14.0_1/Frameworks/Python.framework/Versions/3.14/lib/python3. │
│ 14/concurrent/futures/_base.py:395 in __get_result                                               │
│                                                                                                  │
│   392 │   def __get_result(self):                                                                │
│   393 │   │   if self._exception is not None:                                                    │
│   394 │   │   │   try:                                                                           │
│ ❱ 395 │   │   │   │   raise self._exception                                                      │
│   396 │   │   │   finally:                                                                       │
│   397 │   │   │   │   # Break a reference cycle with the exception in self._exception            │
│   398 │   │   │   │   self = None                                                                │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/tenacity/__init__.py:382 in    │
│ __call__                                                                                         │
│                                                                                                  │
│   379 │   │   │   do = self.iter(retry_state=retry_state)                                        │
│   380 │   │   │   if isinstance(do, DoAttempt):                                                  │
│   381 │   │   │   │   try:                                                                       │
│ ❱ 382 │   │   │   │   │   result = fn(*args, **kwargs)                                           │
│   383 │   │   │   │   except BaseException:  # noqa: B902                                        │
│   384 │   │   │   │   │   retry_state.set_exception(sys.exc_info())  # type: ignore[arg-type]    │
│   385 │   │   │   │   else:                                                                      │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/search/base_search.py:2 │
│ 47 in _search_matching_campsites_available                                                       │
│                                                                                                  │
│    244 │   │   List[AvailableCampsite]                                                           │
│    245 │   │   """                                                                               │
│    246 │   │   matching_campgrounds = []                                                         │
│ ❱  247 │   │   for camp in self.get_all_campsites():                                             │
│    248 │   │   │   if all(                                                                       │
│    249 │   │   │   │   [                                                                         │
│    250 │   │   │   │   │   self._compare_date_overlap(campsite=camp) is True,                    │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/search/search_recreatio │
│ ndotgov.py:247 in get_all_campsites                                                              │
│                                                                                                  │
│   244 │   │   logger.info(f"Searching across {len(self.campgrounds)} campgrounds")               │
│   245 │   │   if self.campsite_metadata is None:                                                 │
│   246 │   │   │   self.campsite_metadata = (                                                     │
│ ❱ 247 │   │   │   │   self.campsite_finder.get_internal_campsite_metadata(                       │
│   248 │   │   │   │   │   facility_ids=[facil.facility_id for facil in self.campgrounds]         │
│   249 │   │   │   │   )                                                                          │
│   250 │   │   │   )                                                                              │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/providers/recreation_do │
│ t_gov/recdotgov_provider.py:772 in get_internal_campsite_metadata                                │
│                                                                                                  │
│   769 │   │   """                                                                                │
│   770 │   │   Retrieve Metadata About all of the underlying Campsites to Search                  │
│   771 │   │   """                                                                                │
│ ❱ 772 │   │   all_campsites: List[RecDotGovCampsite] = self.get_internal_campsites(              │
│   773 │   │   │   facility_ids=facility_ids                                                      │
│   774 │   │   )                                                                                  │
│   775 │   │   all_campsite_df = pd.DataFrame(                                                    │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/providers/recreation_do │
│ t_gov/recdotgov_provider.py:765 in get_internal_campsites                                        │
│                                                                                                  │
│   762 │   │   """                                                                                │
│   763 │   │   all_campsites: List[RecDotGovCampsite] = []                                        │
│   764 │   │   for facility_id in facility_ids:                                                   │
│ ❱ 765 │   │   │   all_campsites += self.paginate_recdotgov_campsites(facility_id=facility_id)    │
│   766 │   │   return all_campsites                                                               │
│   767 │                                                                                          │
│   768 │   def get_internal_campsite_metadata(self, facility_ids: List[int]) -> pd.DataFrame:     │
│                                                                                                  │
│ /Users/jimj/.local/pipx/venvs/camply/lib/python3.14/site-packages/camply/providers/recreation_do │
│ t_gov/recdotgov_camps.py:73 in paginate_recdotgov_campsites                                      │
│                                                                                                  │
│    70 │   │   │   │   params=params,                                                             │
│    71 │   │   │   )                                                                              │
│    72 │   │   │   returned_data = json.loads(response.content)                                   │
│ ❱  73 │   │   │   campsite_response = RecDotGovCampsiteResponse(**returned_data)                 │
│    74 │   │   │   campsites += campsite_response.campsites                                       │
│    75 │   │   │   results += campsite_response.size                                              │
│    76 │   │   │   params.update(start=results)                                                   │
│                                                                                                  │
│ in pydantic.main.BaseModel.__init__:364                                                          │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ValidationError: 1 validation error for RecDotGovCampsiteResponse
campsites
  field required (type=value_error.missing)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions