Article

Cell Types and Data Validation with Smartsheet API

Cell Types and Data Validation with Smartsheet API

Mastering Smartsheet API series — part 6

This post is part of the Mastering Smartsheet API series, a practical companion to the Smartsheet API documentation.

New learning blogs drop regularly — join the API & Developers group in the Smartsheet Community and click Follow → Include in Email Digest to get each new blogpost delivered straight to your inbox via the weekly digest.
 



Your migration finishes without a single error. Then someone opens the Due Date column and finds a few hundred "dates" that are plain text: "TBD", "next week", "Q3". Somewhere upstream, a wall of 1042 errors was "fixed" by switching off strict parsing. The errors went away. So did the structure of the data.

Error 1042 is what Smartsheet returns when a value doesn't fit the column it's going into. It is usually a good sign: the API is telling you something is wrong with the data before the data is stored. This post covers how Smartsheet decides what a value is, what strict and lenient parsing actually do, and how to validate before you send, so you fix the data instead of silencing the error. 

How Smartsheet decides what a value is

Every cell you read back has value (the raw value) and, for some column types, a displayValue (what the user sees). value is a string, a number, or a boolean. Complex types like multi-contact and predecessors use a third attribute, objectValue.

What matters when you write: the column type decides what a valid value looks like, and Smartsheet checks your value against that type. Contact columns are the clearest example. The email address is the value, and the contact's name is the displayValue.

Strict is the default, and 1042 is its answer

By default, a cell value must conform to strict rules for its column type. If it doesn't, the API returns error 1042. A typical response looks like this:

JSON
{
  "errorCode": 1042,
  "message": "The value for cell in column 1234567890123456, 05/31/2023,
did not conform to the strict requirements for type DATE.", "refId": "xxxxxx", "detail": { "index": 0 } }

The message tells you the column, the value, and the type. detail.index tells you which row in your request caused it. In a bulk call with partial success enabled (Part 5), the same information arrives per row inside failedItems.

Strict is a per-call setting, separate from column validation, which is part of the column definition. To switch it off, add "strict": false to the cell object. Here is what changes:

Column type

Strict (default)

Lenient ("strict": false)

DATE

Valid ISO 8601 date, YYYY-MM-DD

Tries ISO 8601 and several locale-specific formats. Anything it can't parse is saved as text

CONTACT_LIST

A valid email address

A valid email is handled the same as strict. Anything else is saved as text

CHECKBOX

Only true or false

The strings "true" and "false" work. Anything else is saved as text

PICKLIST

A string that is one of the column's options

Any text or number is accepted

TEXT_NUMBER

Any text or number, taken literally

Formatted strings like "$5,000" or "50%" are parsed into numbers

DURATION

A valid duration string in the user's locale

Numbers are treated as days. Valid strings become durations. Anything else is text

 

MULTI_CONTACT_LIST, MULTI_PICKLIST, and PREDECESSOR are set through objectValue, which is inherently strict.

The right-hand column is where the trap is. Lenient parsing doesn't make your data valid. It makes the error disappear by storing what it couldn't understand as text.

The quiet failures

Strict mode fails loudly. These are the ways data goes wrong without an error:

  • Lenient parsing. It is more forgiving than most people expect, which is exactly why it is dangerous: it works so often that you stop noticing when it doesn't. We tried it on a test sheet through the Smartsheet MCP server. A CONTACT_LIST cell that received "Maria" kept "Maria", not a contact. Dates are more interesting; see the table below.
  • Numbers out of range. The app supports numbers between -9007199254740992 and 9007199254740992. In strict mode, a value outside that range returns error 1148. In lenient mode it is silently converted to text.
  • Long text. Cell values over 4,000 characters are silently truncated, not rejected. In our test, a 4,500-character value was cut to exactly 4,000 characters, mid-word, and the write still reported success.

Which of these becomes a real date? We wrote each value to a DATE column with "strict": false, through the Smartsheet MCP server:

Value sent

Stored as

A real date?

03/04/2023

2023-03-04

Yes, read as March 4

31/05/2023

2023-05-31

Yes

May 31, 2023

2023-05-31

Yes

31.05.2023

2023-05-31

Yes

2023-5-31

2023-05-31

Yes

TBD

TBD

No, plain text

2023-02-30

2023-02-30

No, plain text that looks like a date

 

Two things stand out. The first row didn't fail, it picked an interpretation, and nothing told us which one. Whether another account would read it as April 3 is something we didn't test. The last row looks like a date and isn't one. Strict mode requires a valid ISO 8601 date, so neither of the last two would have been accepted.

None of these produce an error, which is why the validation has to happen before the request.

Validate before you send

Fetch the column metadata once per sheet (GET /sheets/{sheetId}/columns) and cache it. Then check each value against the column type before it goes into a payload:

Python
import re
from datetime import datetime

ISO_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
EMAIL_SHAPE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
MAX_CELL_CHARS = 4000
MAX_SAFE_NUMBER = 9007199254740992

def validate_cell(column, value):
    """Return a list of problems. An empty list means the value looks safe to send.
    `column` is a column object from GET /sheets/{sheetId}/columns."""
    if value is None:
        return []

    title, col_type = column["title"], column["type"]
    problems = []

    if isinstance(value, str) and len(value) > MAX_CELL_CHARS:
        problems.append(f"{title}: {len(value)} characters, limit is {MAX_CELL_CHARS}")

    if col_type == "DATE":
        try:
            if not ISO_DATE.match(value):
                raise ValueError
            datetime.strptime(value, "%Y-%m-%d")
        except (TypeError, ValueError):
            problems.append(f"{title}: DATE expects YYYY-MM-DD, got {value!r}")

    elif col_type == "CONTACT_LIST":
        if not isinstance(value, str) or not EMAIL_SHAPE.match(value):
            problems.append(f"{title}: CONTACT_LIST expects an email address, got {value!r}")

    elif col_type == "CHECKBOX":
        if not isinstance(value, bool):
            problems.append(f"{title}: CHECKBOX expects true or false, got {value!r}")

    elif col_type == "PICKLIST":
        if value not in column.get("options", []):
            problems.append(f"{title}: {value!r} is not one of the picklist options")

    elif col_type == "TEXT_NUMBER":
        if isinstance(value, (int, float)) and not isinstance(value, bool):
            if abs(value) > MAX_SAFE_NUMBER:
                problems.append(f"{title}: number outside the supported range")

    return problems

The email check only checks the shape. Smartsheet remains the final authority. The point is to catch "Maria" before it becomes a 1042, not to replace the API's validation.

For dates, convert with a format you choose instead of letting anyone guess. 03/04/2023 means different things in different countries:

Python
def to_iso_date(raw, input_format):
    """Convert a date string to YYYY-MM-DD using a format you chose."""
    return datetime.strptime(raw, input_format).date().isoformat()

to_iso_date("05/31/2023", "%m/%d/%Y")   # '2023-05-31'

For contacts, send the email as value and the name as displayValue:

JSON
{ "columnId": 3684419883536260, "value": "maria@example.com", "displayValue": "Maria" }

If displayValue is omitted, Smartsheet makes a best-effort guess at the name from the email address.

Then run the check on a whole batch before it reaches the API, and set aside the rows that fail. This fits the chunking from Part 5:

Python
def split_valid_rows(columns_by_id, rows):
    """rows: [{"cells": }]"""
    good, bad = [], []
    for index, row in enumerate(rows):
        problems = []
        for cell in row["cells"]:
            column = columns_by_id[cell["columnId"]]
            problems += validate_cell(column, cell.get("value"))
        if problems:
            bad.append({"index": index, "problems": problems})
        else:
            good.append(row)
    return good, bad

When you still get a 1042

Treat it as a permanent error (Part 4): retrying the same payload fails the same way. In a bulk job with partial success enabled, send the rows from failedItems to a separate queue, fix the data, and resend. Don't loop, and don't reach for "strict": false as the fix.

If you do need lenient parsing, for example for a one-time import of messy data, audit afterwards. Read the rows back and look for values that don't have the shape of their column:

Python
from datetime import date

def date_cells_that_are_text(rows, date_column_id):
    suspects = []
    for row in rows:
        for cell in row["cells"]:
            if cell["columnId"] == date_column_id and cell.get("value"):
                try:
                    date.fromisoformat(cell["value"])
                except (TypeError, ValueError):
                    suspects.append((row["id"], cell["value"]))
    return suspects

Note what the audit can and can't catch. It flags TBD and 2023-02-30, but it can't tell you that 2023-03-04 was meant to be April 3. Only an explicit input format, like to_iso_date above, closes that gap.

Symptom to cause

What you see

Likely cause

Fix

1042 on a DATE column

Value isn't YYYY-MM-DD

Convert with an explicit format

A "date" that is really text, no error

"strict": false plus an unparseable value

Remove strict: false, convert first

1042 on a CONTACT_LIST column

Value isn't a valid email

Send email as value, name as displayValue

A contact that is really text, no error

"strict": false plus a non-email value

Same as above

1042 on a PICKLIST column

Value isn't one of the options

Check spelling and the column's options

Text cut off mid-sentence

Over 4,000 characters

Check length before sending

And when an AI client is writing

Everything above applies to the Smartsheet MCP Server, because it writes through the same API. Its add_rows tool exposes the same strict flag per cell, and it defaults to true. In the test above, strict was what stopped "Maria" and 05/31/2023: both writes were rejected with a message saying the value did not conform to the strict requirements. The error code itself isn't shown there. Getting the values stored took an explicit "strict": false.

So the safety net is on by default. What to watch is a client that "fixes" a rejection by loosening the rules. If you tried the experiment from Before You Build with MCP, Read This, we'd like to hear what your client did next.

What this post doesn't cover

Multi-contact, multi-picklist, and predecessor cells are set through objectValue, and formula cells have their own rules. See the Cell Reference for the payload shapes.

The data validation checklist

Before you write at scale:

Column types:
  • Column metadata fetched once per sheet and cached
  • Every value checked against its column type before it goes into a payload
  • Dates converted to YYYY-MM-DD with an explicit input format
  • Contacts sent as an email value, with the name in displayValue
Strict parsing:
  • "strict: false is not used to make 1042 errors go away

  • If lenient parsing is deliberate, a read-back audit checks for text where dates or contacts should be
Limits:
  • Text length checked against 4,000 characters
  • Numbers checked against the supported range
Failures:
  • 1042 is treated as a permanent error and never retried unchanged
  • Failed rows go to a separate queue for fixing, not back into the same loop

Catch up on the series

Part 1 — Zero to API: Your First Successful Smartsheet Integration in 30 Minutes

Part 2 — The Smartsheet Data Model: What Every Developer Needs to Know

Part 3 — Authentication Mastery with Smartsheet API

Part 4 — Rate Limits, Retry Logic & Error Handling with Smartsheet API

Part 5 — Bulk Operations at Scale with Smartsheet API

New learning blogs drop regularly — join the API & Developers group in the Smartsheet Community and click Follow → Include in Email Digest to get each new blogpost delivered straight to your inbox via the weekly digest.
 


 

This post is part of the Mastering Smartsheet API series, designed as a practical companion to the Smartsheet API documentation. For questions, visit the Smartsheet Community or the developer forum.

Related Content