{"record":{"id":"19cde31f8b90edd0","repo":"cocoindex-io/cocoindex","slug":"row-row-id-r-attribute-name-k-r-is-reserved","errorCode":null,"errorMessage":"Row {row.id!r}: attribute name {k!r} is reserved.","messagePattern":"Row (.+?): attribute name (.+?) is reserved\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/connectors/turbopuffer/_target.py","lineNumber":228,"sourceCode":"            raise ValueError(\n                f\"Row {row.id!r}: missing vector fields {sorted(missing)}.\"\n            )\n        for name, vec in row.vector.items():\n            out[name] = _vector_to_list(vec)\n    else:\n        vector_field_names = {_DEFAULT_VECTOR_FIELD}\n        if isinstance(row.vector, dict):\n            raise ValueError(\n                f\"Row {row.id!r}: schema declares a single unnamed vector but \"\n                f\"row.vector is a dict.\"\n            )\n        out[_DEFAULT_VECTOR_FIELD] = _vector_to_list(row.vector)\n\n    reserved = {\"id\"} | vector_field_names\n    if row.attributes:\n        for k, v in row.attributes.items():\n            if k in reserved:\n                raise ValueError(f\"Row {row.id!r}: attribute name {k!r} is reserved.\")\n            out[k] = v\n\n    return out\n\n\ndef _vector_type_str(vs: res_schema.VectorSchema) -> str:\n    \"\"\"Render a VectorSchema as turbopuffer's ``[N]fXX`` type string.\"\"\"\n    dt = np.dtype(vs.dtype)\n    if dt == np.float32:\n        suffix = \"f32\"\n    elif dt == np.float16:\n        suffix = \"f16\"\n    else:\n        raise ValueError(\n            f\"Turbopuffer vectors only support float32 or float16, got {dt}.\"\n        )\n    return f\"[{vs.size}]{suffix}\"\n","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/connectors/turbopuffer/_target.py#L210-L246","documentation":"In turbopuffer's wire format, the row id and all vector field names live at the top level of the upsert payload, so a user attribute with one of those names would collide. `_row_to_upsert` builds `reserved = {\"id\"} | vector_field_names` and raises this ValueError if any key in `row.attributes` matches, preventing silent data corruption on write.","triggerScenarios":"Row(id=1, vector=..., attributes={\"id\": \"x\"}) or attributes containing a key equal to a declared vector field name (e.g. \"title_vec\") when reconcile serializes the row.","commonSituations":"Storing source document metadata that includes an 'id' or 'vector'-like field; bulk-uploading records whose field names came from a CSV/JSON that already contains 'id'; accidentally dumping whole objects into attributes instead of picking non-reserved keys.","solutions":["Rename the attribute key to something not in {\"id\"} ∪ declared vector field names, e.g. \"doc_id\" or \"source_id\".","Filter or remap reserved keys before constructing Row: drop or prefix them in your mapping function.","If the attribute genuinely duplicates the row id, just remove it — the id is already written at the top level."],"exampleFix":"// before\nRow(id=doc[\"id\"], vector=vec, attributes=doc)  # doc contains 'id' and 'title_vec'\n\n// after\nattrs = {k: v for k, v in doc.items() if k not in {\"id\", \"title_vec\"}}\nRow(id=doc[\"id\"], vector=vec, attributes=attrs)","handlingStrategy":"validation","validationCode":"reserved = {\"id\"} | set(schema.vectors.vectors)\nbad = set(row.attributes or {}) & reserved\nassert not bad, f\"Reserved attribute keys: {bad}\"","typeGuard":null,"tryCatchPattern":"try:\n    await component.reconcile(row)\nexcept ValueError as e:\n    m = re.search(r\"attribute name (.+?) is reserved\", str(e))\n    if m:\n        row = rename_attribute(row, ast.literal_eval(m.group(1)))\n    else:\n        raise","preventionTips":["Sanitize external metadata (CSV/JSON) by renaming or dropping 'id' and vector-field-named keys before building Rows","Prefix attribute keys with a namespace like 'meta_' when ingesting arbitrary records","Keep the reserved-name list next to the schema definition and share it with the sanitizer"],"tags":["python","reserved-key","turbopuffer"],"backgroundTag":"invalid-argument-value","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}