{"record":{"id":"2cd789aee9870fb3","repo":"mvanhorn/last30days-skill","slug":"exc","errorCode":null,"errorMessage":"{exc}","messagePattern":"\\{exc\\}","errorType":"exception","errorClass":"HtmlPublishError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/html_publish.py","lineNumber":59,"sourceCode":"        payload[\"password\"] = password\n\n    request = Request(\n        endpoint,\n        data=json.dumps(payload).encode(\"utf-8\"),\n        headers={\"Content-Type\": \"application/json\", \"Accept\": \"application/json\"},\n        method=\"POST\",\n    )\n    open_fn = opener or urlopen\n    try:\n        with open_fn(request, timeout=timeout) as response:\n            body = response.read().decode(\"utf-8\")\n    except HTTPError as exc:\n        detail = exc.read().decode(\"utf-8\", errors=\"replace\")\n        raise HtmlPublishError(_error_message(exc.code, detail)) from exc\n    except URLError as exc:\n        raise HtmlPublishError(str(exc.reason)) from exc\n    except OSError as exc:\n        raise HtmlPublishError(str(exc)) from exc\n\n    try:\n        result = json.loads(body)\n    except json.JSONDecodeError as exc:\n        raise HtmlPublishError(\"publish endpoint returned non-JSON response\") from exc\n    if not isinstance(result, dict):\n        raise HtmlPublishError(\"publish endpoint returned unexpected JSON response\")\n\n    url = result.get(\"url\")\n    if not isinstance(url, str) or not url.startswith(\"https://\"):\n        raise HtmlPublishError(\"publish endpoint response did not include a valid url\")\n    return result\n\n\ndef publish_html_documents(\n    documents: Mapping[str, str],\n    *,\n    password: str | None = None,","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/html_publish.py#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nresult = publish_html(big_html)  # default timeout=30\n\n# after\nresult = publish_html(big_html, timeout=120)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"import socket\n\ntry:\n    result = publish_html(html)\nexcept HtmlPublishError as e:\n    cause = e.__cause__\n    if isinstance(cause, (socket.timeout, ConnectionResetError)):\n        result = publish_html(html, timeout=120)  # one retry with a longer timeout\n    else:\n        raise","preventionTips":["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."],"tags":["network","timeout","html-publish","transient"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}