{"record":{"id":"61d42794e5328b6f","repo":"MemPalace/mempalace","slug":"artifact-ids-must-be-a-list-of-artifact-id-strings","errorCode":null,"errorMessage":"artifact_ids must be a list of artifact id strings","messagePattern":"artifact_ids must be a list of artifact id strings","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/logstream.py","lineNumber":424,"sourceCode":"        \"\"\"\n        type = _sanitize_event_type(type)\n        stream = _sanitize_routing(stream, \"stream\")\n        room = _sanitize_routing(room, \"room\")\n        from_agent = _sanitize_routing(from_agent, \"from_agent\")\n        to_agent = _sanitize_routing(to_agent, \"to_agent\", required=False)\n        correlation_id = _sanitize_routing(correlation_id, \"correlation_id\", required=False)\n        branch = _sanitize_routing(branch, \"branch\", required=False)\n        base_commit = _sanitize_routing(base_commit, \"base_commit\", required=False)\n        status = _sanitize_status(status)\n        body = _sanitize_body(body, self.max_body_bytes)\n        metadata_json = _sanitize_metadata(metadata)\n\n        if artifact_ids is None:\n            artifact_ids = []\n        if not isinstance(artifact_ids, list) or not all(\n            isinstance(a, str) and a for a in artifact_ids\n        ):\n            raise ValueError(\"artifact_ids must be a list of artifact id strings\")\n        artifact_ids = list(dict.fromkeys(artifact_ids))  # dedup, keep order\n\n        event_id = _new_id(\"evt\")\n        created_at = _utc_now_iso()\n        hlc = self._clock.tick()\n\n        with self._lock:\n            conn = self._conn()\n            with conn:\n                for artifact_id in artifact_ids:\n                    found = conn.execute(\n                        \"SELECT 1 FROM artifacts WHERE id = ?\", (artifact_id,)\n                    ).fetchone()\n                    if not found:\n                        raise ValueError(\n                            f\"artifact_ids references unknown artifact {artifact_id!r}\"\n                        )\n                cursor = conn.execute(","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/logstream.py#L406-L442","documentation":"The artifact_ids argument of append_event must be a list (or None) whose elements are all non-empty strings. Anything else — a single id string, a tuple, a set, a list with ints or empty strings — is rejected before any database lookup. ids must already exist as artifacts (see the follow-up unknown-artifact error for semantic failures).","triggerScenarios":"append_event(artifact_ids='art_20250814_ab12') (bare string, not a list); artifact_ids=('a','b') (tuple); artifact_ids=[123]; artifact_ids=['art1', ''] (empty element); artifact_ids={'a','b'} (set).","commonSituations":"Callers with a single artifact forgetting the brackets; code paths that build ids from int rowids; JSON deserialization yielding a list of numbers; defensive conversion from other collections left as generator/tuple.","solutions":["Always pass a list of strings: artifact_ids=[art['id']].","Coerce before calling: artifact_ids=[str(a) for a in ids if a].","Pass artifact_ids=None (or omit) when the event references no artifacts — it defaults to []."],"exampleFix":"// before\nevt = ls.append_event(..., artifact_ids=art[\"id\"])\n// after\nevt = ls.append_event(..., artifact_ids=[art[\"id\"]])","handlingStrategy":"type-guard","validationCode":"def as_artifact_ids(value):\n    if value is None:\n        return []\n    if isinstance(value, str):\n        value = [value]\n    if not isinstance(value, (list, tuple)):\n        raise TypeError(\"artifact_ids must be a list of id strings\")\n    return [str(a) for a in value if a]\n\nartifact_ids = as_artifact_ids(raw_ids)","typeGuard":"def is_valid_artifact_ids(v) -> bool:\n    return v is None or (isinstance(v, list) and all(isinstance(a, str) and a for a in v))","tryCatchPattern":"try:\n    evt = ls.append_event(..., artifact_ids=artifact_ids)\nexcept ValueError as e:\n    if \"must be a list\" in str(e):\n        evt = ls.append_event(..., artifact_ids=[artifact_ids])\n    else:\n        raise","preventionTips":["Always wrap single ids in brackets at the call site.","Use the ids exactly as returned by put_artifact — never reconstruct or truncate them."],"tags":["validation","logstream","artifact-ids","type-error"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}