PostgreSQL: string parameters silently truncated to 8000 chars in raw SQL queries

We recently spent a while chasing what looked like a pgvector limitation and it turned out to be something more general, so sharing in case it helps others — and to ask if this is intended behavior.

Any string parameter longer than 8000 characters passed to a raw SQL query (SqlList/SqlScalar/ExecuteSql with anonymous params) gets silently truncated to exactly 8000 chars on PostgreSQL — no exception, no warning.

Repro (ServiceStack.OrmLite.PostgreSQL 10.0.6, Npgsql, .NET 10):

var db = new OrmLiteConnectionFactory(connStr, PostgreSqlDialect.Provider).Open();
var longText = new string('x', 9000);
var roundTrip = db.SqlScalar<string>("SELECT @s::text", new { s = longText });
// roundTrip.Length == 8000 — silently truncated

What we believe is happening: StringConverter’s parameterless ctor defaults StringLength = 8000, and InitDbParam sets p.Size = StringLength on any string parameter without an explicit size. Npgsql honors Size by truncating longer values, so anything past 8000 chars is quietly dropped.

How we hit it: passing pgvector embeddings as @embedding::vector — a 3072-dim embedding’s string form is ~60KB, so it truncated to 8000 and Postgres reported 22P02: invalid input syntax for type vector: "[...0.6" (the value ends mid-float at the 8000-char mark). Small vectors work fine as parameters, so for a long time we thought vectors simply couldn’t be parameterized and interpolated them into the SQL string instead. The case that worries us more is a plain long text value written through a raw UPDATE — the truncated value is valid, so the row just silently loses its tail.

Our workaround is adding the parameter via a command filter with p.Size = value.Length, which works fine.

Is this intended behavior for PostgreSQL, and is there a supported way to opt out per-provider? What surprised us is that the provider’s own PostgreSqlStringConverter maps string columns to limitless TEXT, so we didn’t expect an 8000-char cap on the parameter side.

1 Like

You can indicate to use a the maximum text size with:

[StringLength(StringLengthAttribute.MaxText)]
public string? Content { get; set; }

This is configurable per provider, which for PostgreSQL will use a TEXT column.

When needed you can also configure the configured StringConverter directly:

StringConverter converter = OrmLiteConfig.DialectProvider.GetStringConverter();
converter.StringLength = ....;

Thanks — the wrinkle is this was a raw SQL query with anonymous params (db.SqlScalar<string>("SELECT @s::text", new { s })), so there’s no property to put [StringLength] on. Is GetStringConverter().StringLength = ... the recommended way to lift the default for raw-SQL params on PostgreSQL — and would you consider having the overflow throw or log rather than truncate silently, since on a TEXT column the truncated value is valid and the data loss is invisible?

There’s an overload that takes a raw IDbDataParameter, e.g:

var s = db.CreateParam("s",s);
s.Size = 9000;
var roundTrip = db.SqlScalar<string>("SELECT @s::text", new { s });