Skip to content

[Bug]: REST API v2 entities/insert silently coerces scalar types (string→Int64, int→VarChar, string→Bool); gRPC rejects all #52310

Description

@yihui504

Is there an existing issue for this?

  • I have searched the existing issues

Environment

- Milvus version: v3.0.0 (release 2026-07-29, commit f46a0328558be155d11266a1a2b90602ccc9b366)
- Deployment mode: standalone (Docker, `milvusdb/milvus:v3.0.0`)
- MQ type: N/A
- SDK version: REST API v2 only (curl). pymilvus used for gRPC comparison.
- OS: Windows 11 (Docker Desktop)
- CPU/Memory: N/A
- GPU: None

Current Behavior

The REST API v2 entities/insert endpoint silently coerces compatible-but-wrong types across scalar fields:

Input type Field type REST v2 Stored value gRPC
"123" (string) Int64 accepted 123 rejected
123 (int) VarChar accepted "123" rejected
"true" (string) Bool accepted true rejected
"1.5" (string) Float accepted 1.5 rejected

Issue #47766 fixed type validation on the gRPC path (milestone 2.6.14, closed 2026-05-08), but the REST API v2 path still exhibits the same behavior in v3.0.0.

The Number Field documentation defines each scalar type with a specific data type code (INT64, BOOL, FLOAT, etc.) and describes them as storing specific value types — integers, booleans, or floating-point numbers. Cross-type coercion (string → Int64, int → VarChar, string → Bool) is not documented behavior.

Expected Behavior

Scalar field types should enforce strict type checking on both gRPC and REST paths. The fix for #47766 should extend to the REST API.

Steps To Reproduce

# 1. Create collection with mixed-type fields (via pymilvus for schema definition)
python -c "
from pymilvus import MilvusClient, DataType
mc = MilvusClient(uri='http://localhost:19530')
schema = mc.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field('id', DataType.INT64, is_primary=True)
schema.add_field('vector', DataType.FLOAT_VECTOR, dim=4)
schema.add_field('int64_f', DataType.INT64, nullable=True)
schema.add_field('varchar_f', DataType.VARCHAR, max_length=64, nullable=True)
schema.add_field('bool_f', DataType.BOOL, nullable=True)
mc.create_collection('test_coerce', schema=schema)
"

# 2. Insert cross-type values via REST
# string → Int64
curl -X POST http://localhost:19530/v2/vectordb/entities/insert \
  -H "Content-Type: application/json" \
  -d '{"collectionName":"test_coerce","data":[{"id":1,"vector":[0.1,0.2,0.3,0.4],"int64_f":"123"}]}'
# Response: {"code":0,...} — accepted, stored as 123

# int → VarChar
curl -X POST http://localhost:19530/v2/vectordb/entities/insert \
  -H "Content-Type: application/json" \
  -d '{"collectionName":"test_coerce","data":[{"id":2,"vector":[0.1,0.2,0.3,0.4],"varchar_f":123}]}'
# Response: {"code":0,...} — accepted, stored as "123"

# string → Bool
curl -X POST http://localhost:19530/v2/vectordb/entities/insert \
  -H "Content-Type: application/json" \
  -d '{"collectionName":"test_coerce","data":[{"id":3,"vector":[0.1,0.2,0.3,0.4],"bool_f":"true"}]}'
# Response: {"code":0,...} — accepted, stored as true

# 3. Control: non-numeric string → Int64 (correctly rejected)
curl -X POST http://localhost:19530/v2/vectordb/entities/insert \
  -H "Content-Type: application/json" \
  -d '{"collectionName":"test_coerce","data":[{"id":4,"vector":[0.1,0.2,0.3,0.4],"int64_f":"abc"}]}'
# Response: {"code":1804,...} — rejected (only non-coercible strings are caught)

Milvus Log

REST v2 path (accepted - no error logged):

[2026/08/07 14:14:50.755 Z] [GIN] [/v2/vectordb/entities/insert] [traceID=8eec52baf553ea87289e8c437d18ef92] [code=200] [latency=4.183428ms] [client=172.18.0.1] [method=POST] [error=]
[2026/08/07 14:15:30.607 +00:00] [INFO] [delegator/delegator_data.go:248] ["insert into growing segment"] [collectionID=468218553071830183] [rowCount=1]

Note the empty [error=] field - the REST path treats the invalid input as successful.

gRPC path (correctly rejected - pymilvus raises client-side):

<MilvusException: code=1100, message="field type mismatch">

Run docker logs milvus-standalone for full logs.

Anything else?

  • [Bug]: Data type validation missing - accepts integer into string field #47766: same root cause, gRPC path (FIXED 2.6.14). REST v2 path not fixed.
  • Number Field docs: defines INT64/INT16/BOOL/DOUBLE as distinct numeric types with specific value semantics — cross-type coercion is not documented.
  • Control case (step 3) shows the REST path does validate non-coercible types — it only misses types where silent coercion "works" (numeric strings → int, int → string, string → bool).

Metadata

Metadata

Labels

kind/bugIssues or changes related a bugneeds-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions