Skip to content

Commit 7d34b5e

Browse files
MaorDavidzonclaude
andcommitted
Remove broken background spec fetch; use synchronous timeout
The previous 3-second-timeout-with-background-continuation design was broken: the background thread was a daemon, so when the 3s join timeout expired and the CLI raised, the process exited and killed the thread before it could cache the spec. Users on slow networks would hit the timeout on every invocation with no path forward. Replace with a straight synchronous fetch that relies on the HTTP client's own timeout. First call on a cold cache may take a few seconds; every subsequent call within the TTL is served from disk. Update README and docstring to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent baad150 commit 7d34b5e

File tree

2 files changed

+6
-35
lines changed

2 files changed

+6
-35
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ cycode platform projects list --page-size 100 | jq '.items[].name'
663663
664664
- **Read-only today.** Only `GET` endpoints are exposed in this beta.
665665
- **Spec-driven.** Adding a new endpoint to the API surfaces it automatically the next time the cache is refreshed.
666-
- **No bundled spec.** The first `cycode platform` invocation after install (or after the 24h cache expires) will perform a network fetch with a 3-second timeout; if it times out, it continues fetching in the background so the next run is fast.
666+
- **No bundled spec.** The first `cycode platform` invocation after install (or after the 24h cache expires) performs a network fetch. On slow connections this first call may take a few seconds; subsequent calls are near-instant until the cache expires.
667667
- **Override the cache TTL** with `CYCODE_SPEC_CACHE_TTL=<seconds>`.
668668
669669

cycode/cli/apps/api/openapi_spec.py

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import json
44
import os
5-
import threading
65
import time
76
from pathlib import Path
87
from typing import Optional
@@ -20,16 +19,14 @@
2019

2120
_OPENAPI_SPEC_PATH = '/v4/api-docs/cycode-api-swagger.json'
2221

23-
# Short timeout for spec fetch during CLI startup to avoid blocking the regular flow.
24-
# If it times out, a background thread fetches the spec for the next run.
25-
_SPEC_FETCH_TIMEOUT_SECONDS = 3
26-
2722

2823
def get_openapi_spec(client_id: Optional[str] = None, client_secret: Optional[str] = None) -> dict:
2924
"""Get the OpenAPI spec, using cache if fresh, otherwise fetching from API.
3025
31-
Uses a short timeout to avoid blocking CLI startup. If the fetch times out,
32-
a background thread continues the fetch so the cache is warm for the next run.
26+
The spec is only fetched when the user actually invokes `cycode platform ...`.
27+
Fetch uses the HTTP client's default timeout; on a slow connection the first
28+
invocation will block accordingly. Once cached, subsequent invocations within
29+
the TTL are near-instant.
3330
3431
Args:
3532
client_id: Optional client ID override (from CLI flags).
@@ -45,7 +42,7 @@ def get_openapi_spec(client_id: Optional[str] = None, client_secret: Optional[st
4542
if cached is not None:
4643
return cached
4744

48-
return _fetch_spec_with_timeout(client_id, client_secret)
45+
return _fetch_and_cache_spec(client_id, client_secret)
4946

5047

5148
def _load_cached_spec() -> Optional[dict]:
@@ -84,32 +81,6 @@ def resolve_credentials(client_id: Optional[str] = None, client_secret: Optional
8481
return client_id, client_secret
8582

8683

87-
def _fetch_spec_with_timeout(client_id: Optional[str] = None, client_secret: Optional[str] = None) -> dict:
88-
"""Fetch spec with a short timeout. If it times out, fetch in background for next run."""
89-
result: dict = {}
90-
error: list = []
91-
92-
def _fetch() -> None:
93-
try:
94-
result['spec'] = _fetch_and_cache_spec(client_id, client_secret)
95-
except Exception as e:
96-
error.append(e)
97-
98-
thread = threading.Thread(target=_fetch, daemon=True)
99-
thread.start()
100-
thread.join(timeout=_SPEC_FETCH_TIMEOUT_SECONDS)
101-
102-
if thread.is_alive():
103-
# Fetch is still running in the background - it will cache the result for next run
104-
logger.debug('OpenAPI spec fetch timed out after %ds, continuing in background', _SPEC_FETCH_TIMEOUT_SECONDS)
105-
raise OpenAPISpecError('OpenAPI spec fetch timed out. API commands will be available on the next run.')
106-
107-
if error:
108-
raise error[0]
109-
110-
return result['spec']
111-
112-
11384
def _fetch_and_cache_spec(client_id: Optional[str] = None, client_secret: Optional[str] = None) -> dict:
11485
"""Fetch OpenAPI spec from API and cache to disk.
11586

0 commit comments

Comments
 (0)