mvanhorn/last30days-skill · error · HtmlPublishError
{exc}
Error message
{exc} What it means
Raised by publish_html when the request fails with a bare OSError that is neither HTTPError nor URLError — message is str(exc). In urllib terms this catches lower-level socket/timeout problems such as socket.timeout during read, ConnectionResetError, or SSL read failures that do not surface as URLError.
Source
Thrown at skills/last30days/scripts/lib/html_publish.py:59
payload["password"] = password
request = Request(
endpoint,
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json", "Accept": "application/json"},
method="POST",
)
open_fn = opener or urlopen
try:
with open_fn(request, timeout=timeout) as response:
body = response.read().decode("utf-8")
except HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise HtmlPublishError(_error_message(exc.code, detail)) from exc
except URLError as exc:
raise HtmlPublishError(str(exc.reason)) from exc
except OSError as exc:
raise HtmlPublishError(str(exc)) from exc
try:
result = json.loads(body)
except json.JSONDecodeError as exc:
raise HtmlPublishError("publish endpoint returned non-JSON response") from exc
if not isinstance(result, dict):
raise HtmlPublishError("publish endpoint returned unexpected JSON response")
url = result.get("url")
if not isinstance(url, str) or not url.startswith("https://"):
raise HtmlPublishError("publish endpoint response did not include a valid url")
return result
def publish_html_documents(
documents: Mapping[str, str],
*,
password: str | None = None,View on GitHub (pinned to c7460f6114)
Solutions
- Retry once — resets and read timeouts are frequently transient.
- Pass a larger timeout= to publish_html for large documents (e.g. timeout=120).
- Reduce payload size (minify HTML) if timeouts are consistent.
- Inspect e.__cause__ to distinguish socket.timeout / ConnectionResetError and log accordingly.
Example fix
# before result = publish_html(big_html) # default timeout=30 # after result = publish_html(big_html, timeout=120)
Defensive patterns
Strategy: retry
Try / catch
import socket
try:
result = publish_html(html)
except HtmlPublishError as e:
cause = e.__cause__
if isinstance(cause, (socket.timeout, ConnectionResetError)):
result = publish_html(html, timeout=120) # one retry with a longer timeout
else:
raise Prevention
- Pass an explicit timeout= sized to the document (large HTML needs more than the 30s default).
- Treat resets/read timeouts as transient: retry once before surfacing to the user.
- Minify very large HTML to cut upload time.
When it happens
Trigger: The server accepts the connection then stalls past timeout=30 (default), producing a socket timeout; the connection is reset mid-upload of a large HTML body; SSL handshake data read fails.
Common situations: Publishing very large documents over slow links; flaky Wi-Fi; an endpoint that hangs on oversized payloads; default 30s timeout too short for big uploads on high-latency networks.
Related errors
- Gemini request failed: {exc}
- {status}: {message}
- {exc.reason}
- publish endpoint returned non-JSON response
- HTML content is empty
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/2cd789aee9870fb3.
Report an issue: GitHub.