{"record":{"id":"6784f8ef2eada040","repo":"hacksider/Deep-Live-Cam","slug":"embeddings-must-not-be-empty","errorCode":null,"errorMessage":"embeddings must not be empty","messagePattern":"embeddings must not be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"modules/cluster_analysis.py","lineNumber":9,"sourceCode":"import numpy as np\nfrom sklearn.cluster import KMeans\nfrom typing import Any\n\n\ndef find_cluster_centroids(embeddings, max_k=10) -> Any:\n    n_samples = len(embeddings)\n    if n_samples == 0:\n        raise ValueError(\"embeddings must not be empty\")\n    if max_k < 1:\n        raise ValueError(\"max_k must be at least 1\")\n\n    max_k = min(max_k, n_samples)\n    if max_k == 1:\n        kmeans = KMeans(n_clusters=1, random_state=0)\n        kmeans.fit(embeddings)\n        return kmeans.cluster_centers_\n\n    inertia = []\n    cluster_centroids = []\n    K = range(1, max_k+1)\n\n    for k in K:\n        kmeans = KMeans(n_clusters=k, random_state=0)\n        kmeans.fit(embeddings)\n        inertia.append(kmeans.inertia_)\n        cluster_centroids.append({\"k\": k, \"centroids\": kmeans.cluster_centers_})","sourceCodeStart":1,"sourceCodeEnd":27,"githubUrl":"https://github.com/hacksider/Deep-Live-Cam/blob/987f6b392b1740623b3fa8a5cb46fdd0b7e185b9/modules/cluster_analysis.py#L1-L27","documentation":"ValueError raised by find_cluster_centroids in modules/cluster_analysis.py when the embeddings sequence passed in has length 0. The function needs at least one sample to fit sklearn KMeans, so it fails fast before touching KMeans rather than letting sklearn raise a less clear error. It is a pure input-contract error: the embeddings argument itself is empty, not malformed.","triggerScenarios":"Calling find_cluster_centroids([]), find_cluster_centroids(np.empty((0, 768))), or any embeddings list produced by a pipeline stage that returned zero items (e.g. all frames filtered out, zero faces detected, empty batch from a database query). len(embeddings) == 0 at function entry is the only trigger.","commonSituations":"Upstream embedding extraction silently returning an empty list; a filter step removing all candidates before clustering; first run of a pipeline on an empty dataset; passing a generator result that was already consumed; None-vs-[] confusion where an empty list is forwarded instead of aborting.","solutions":["Check len(embeddings) > 0 at the call site before invoking find_cluster_centroids and skip/short-circuit clustering when there is nothing to cluster.","Trace upstream: log the size of the embedding batch right after it is produced to find which stage emptied it.","If an empty input is a legitimate case for your flow, handle it explicitly (return [] or skip clustering) instead of relying on the exception."],"exampleFix":"// before\ncentroids = find_cluster_centroids(embeddings)\n\n// after\nif not embeddings:\n    return []\ncentroids = find_cluster_centroids(embeddings)","handlingStrategy":"validation","validationCode":"if not embeddings or len(embeddings) == 0:\n    # nothing to cluster; skip or return empty result\n    return []\ncentroids = find_cluster_centroids(embeddings)","typeGuard":"def has_samples(embeddings) -> bool:\n    try:\n        return len(embeddings) > 0\n    except TypeError:\n        return False","tryCatchPattern":"try:\n    centroids = find_cluster_centroids(embeddings)\nexcept ValueError as e:\n    if \"must not be empty\" in str(e):\n        centroids = []  # explicit empty-policy, not a silent fallback\n    else:\n        raise","preventionTips":["Log embedding-batch size at the producing stage so empties are caught where they originate.","Treat an empty batch as a skip condition in the pipeline, never forward it to clustering.","Add a unit test calling find_cluster_centroids with [] to pin the contract."],"tags":["python","sklearn","clustering","validation","input-validation"],"backgroundTag":null,"analyzedSha":"987f6b392b1740623b3fa8a5cb46fdd0b7e185b9","analyzedAt":"2026-08-14T19:48:25.860Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}