text
A text field has no empty string: writing "" stores null, so is "" and is None are one filter; matching is case-insensitive, whitespace is stripped, and a non-string 400s.
Data type text. Probed on Version.sg_department (stock, editable), with description,
client_code, code and cached_display_name as controls. All five report data_type: text,
editable: True; only code is mandatory. None is unique.
Read A plain JSON string under attributes, never under relationships. Absent means null, and
null is the only empty state a read can return.
keys on the row: ['attributes', 'id', 'links', 'relationships', 'type']
attributes: {"code": "sh010_comp_v001", "sg_department": null,
"description": "<free text>", "cached_display_name": "sh010_comp_v001"}
python types: [('code','str'), ('sg_department','NoneType'), ('description','str')]
relationships: []
over 100 rows, sg_department is: {'null': 100, 'empty string': 0, 'value': 0}
Write POST /entity/versions on create, PUT /entity/versions/<id> to update; both take the field
as a bare string in the body. Only String and NilClass are accepted.
| sent | status | reads back as |
|---|---|---|
' padded ' |
200 | 'padded'; both ends stripped |
' ' (whitespace only) |
200 | None; stripped to '', then nulled |
'line1\nline2' |
200 | 'line1\nline2'; newlines survive a one-line field |
'héllo ✨ 漢字' |
200 | 'héllo ✨ 漢字' |
'<b>&</b> "q" \'s\'' |
200 | unchanged; stored verbatim, no escaping |
'x' * 5000 |
200 | len=5000 |
'x' * 100000 |
200 | len=100000; no length cap found |
12345 (int) |
400 | API update() Version.sg_department expected [String, NilClass] data type(s) but got Integer: 12345 |
True (bool) |
400 | ... but got TrueClass: true |
['a','b'] (list) |
400 | ... but got Array: ["a", "b"] |
{'a': 1} (dict) |
400 | ... but got Hash: {"a" => 1} |
Clear
| sent | result |
|---|---|
null |
200, reads back None |
"" |
200, reads back None; coerced, not stored |
| key omitted from the PUT | 200, field unchanged; set to 'lighting', then a PUT of description alone, still reads 'lighting' |
Four rows differing only in how the field was left:
| body | reads back |
|---|---|
{"sg_department": "lighting"} |
'lighting' |
{"sg_department": ""} |
None |
{"sg_department": None} |
None |
{} |
None |
The last three are indistinguishable on read; there is no "set but blank" state.
Filter POST /entity/versions/_search, Content-Type application/vnd+shotgun.api3_array+json
(probe 004). A bogus operator 400s and names the whole vocabulary:
title: "API read() Version.sg_department's 'text' data type doesn't support
'definitely_not_an_operator' 'relation'"
source: {"Version.sg_department": " ... Value: {"path" => "sg_department",
"relation" => "definitely_not_an_operator", "values" => [nil]}
Valid relations: ["contains", "not_contains", "is", "is_not",
"starts_with", "ends_with", "in", "not_in"]"}
is, is_not, contains, not_contains, starts_with and ends_with take a scalar string;
in and not_in take a list. Which of the four rows above each filter returns:
| operator | value | matches |
|---|---|---|
is |
'lighting' |
value |
is |
'LIGHTING' |
value; case-blind |
is |
None |
empty, null, omitted |
is |
'' |
empty, null, omitted; identical to is None |
is_not |
'lighting' |
empty, null, omitted; null rows included |
is_not |
None or '' |
value |
contains |
'ight' |
value |
contains |
'IGHT' |
value; case-blind |
contains |
'' |
value; "has any value", not everything |
not_contains |
'ight' |
empty, null, omitted |
starts_with |
'light' |
value |
ends_with |
'ing' |
value |
in |
['lighting','comp'] |
value |
in |
'lighting' |
value; a bare string is accepted where a list is expected, and matched whole. A fifth row holding 'g' is not returned |
in |
[''] |
nothing, unlike is '' |
in |
[None] |
400 code 104 Read failed for entity type [Version], source null, detail null |
not_in |
['lighting'] |
empty, null, omitted |
is |
'ZZZNOPE' |
nothing; the baseline is 4 |
contains |
'ZZZNOPE' |
nothing |
starts_with |
'ZZZNOPE' |
nothing |
in |
'ZZZNOPE' |
nothing |
Traps
- Writing
""returns 200 and storesnull. A client round-tripping a form field cannot distinguish "user cleared it" from "never set"; encode that distinction in a sentinel string. - Every operator is case-insensitive, including
is. There is no case-sensitive text match. Filter broadly and re-check the case client-side. is_notandnot_containsreturn the null rows, sois_not Xis not the complement ofis X.is ''matches exactly the rowscontains ''skips.- Leading and trailing whitespace is stripped on write, silently, so
' 'is stored asnull. cached_display_namereportseditable: Trueand accepts a write at 200, then discards it. It mirrorscode, and re-reads as the newcodeafter a rename. Never write it.in [None]teaches nothing: code 104,source: null,detail: null. Useis None.