{"record":{"id":"34169fb48f395fbe","repo":"mvanhorn/last30days-skill","slug":"html-content-is-empty","errorCode":null,"errorMessage":"HTML content is empty","messagePattern":"HTML content is empty","errorType":"exception","errorClass":"HtmlPublishError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/html_publish.py","lineNumber":37,"sourceCode":"class HtmlPublishBatchResult(dict[str, dict[str, Any]]):\n    \"\"\"Successful document publishes plus an optional later failure.\"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        self.error: HtmlPublishError | None = None\n\n\ndef publish_html(\n    html_content: str,\n    *,\n    password: str | None = None,\n    endpoint: str = DEFAULT_ENDPOINT,\n    opener: Callable[..., Any] | None = None,\n    timeout: int = 30,\n) -> dict[str, Any]:\n    \"\"\"Publish a single HTML document and return the provider response.\"\"\"\n    if not html_content.strip():\n        raise HtmlPublishError(\"HTML content is empty\")\n\n    payload: dict[str, str] = {\"html_content\": html_content}\n    if password is not None:\n        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","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/html_publish.py#L19-L55","documentation":"Raised by publish_html in html_publish.py when html_content is empty or contains only whitespace. The hosted publish endpoint (default https://api.ht-ml.app/v1/sites) would reject an empty document anyway, so the client fails fast before any network I/O. It is an HtmlPublishError (subclass of RuntimeError), the single exception type for the whole publish flow.","triggerScenarios":"Calling publish_html(\"\") or publish_html(\"   \\n\"); passing a rendered string from a template that produced nothing (empty topic data, renderer bug); passing a variable that was never assigned because an upstream render step failed silently.","commonSituations":"A publish step wired into a pipeline where the render stage returned an empty string on failure instead of raising; conditional content that is all-falsy for a given dataset; trailing refactor that renamed the render output variable.","solutions":["Check the render output before publishing: if the renderer returned empty, fix the renderer/data rather than the publish call.","Guard the call site: skip publish when content is blank and surface a clearer upstream error.","If empty documents are legitimately possible, branch to a fallback page or abort the batch with a specific message."],"exampleFix":"# before\npublish_html(rendered)  # rendered == \"\"\n\n# after\nif not rendered.strip():\n    raise RuntimeError(\"renderer produced empty HTML for this topic\")\npublish_html(rendered)","handlingStrategy":"validation","validationCode":"def is_publishable(html: str) -> bool:\n    return isinstance(html, str) and bool(html.strip())","typeGuard":"def is_nonempty_html(content: object) -> bool:\n    return isinstance(content, str) and len(content.strip()) > 0","tryCatchPattern":"try:\n    publish_html(html)\nexcept HtmlPublishError as e:\n    if str(e) == \"HTML content is empty\":\n        # fix the upstream renderer; do not retry the publish\n        ...","preventionTips":["Make the render stage raise on empty output instead of returning an empty string.","Assert non-empty content in pipeline code between render and publish.","Skip optional publishes with a warning rather than letting a blank topic abort a batch."],"tags":["validation","html-publish","fail-fast"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}