- 2026.07.29 What Breaks When Apps Script Projects Sprawl
- 2026.07.16 Why a Daily Apps Script Trigger Suddenly Stops
- 2026.07.15 What Breaks When Customer Data Lives in Browser Storage
- 2026.07.07 When the Browser Throws Your Data Away: The Green Checkmark Lies
- 2026.07.07 Installed ≠ Working: the Backup Trigger Never Ran Once
- 2026.07.07 GAS Has No Gradual Rollout: One 'Correct' Check Locks Out Every User
- 2026.07.07 Sheets Turns Your Text Into a Date: When '2026-06' Blocks Completion Forever
- 2026.06.18 An App Where Many Write to One Sheet: Concurrency, Duplicates, and Export Design
- 2026.06.12 Don't Read Cells One at a Time: Service Calls Are the Real Cost
- 2026.06.11 Stuffing JSON or Images into a Sheet Cell Will Break It You are here
- 2026.06.10 One Deploy Setting Decides Your Security Model: Execute as / Access
- 2026.06.09 The 6-Minute GAS Limit: Chunk and Resume Before It Kills You
- 2026.06.08 PropertiesService Is Not a Database: the 9KB Wall
- 2026.06.07 Why Saving a Screen as an Image Freezes on Mobile
- 2026.06.06 Concurrent Writes to the Same Sheet Break: LockService and the 30-Run Ceiling
- 2026.06.05 Your /exec URL Must Not Change on Every Redeploy
- 2026.06.04 External Calls Fail Sometimes: Exponential Backoff and a Retry Budget
- 2026.06.03 HtmlService Apps: Shell First, Then Async Load and Mobile Back
- 2026.06.02 When to Graduate from GAS: Limit Signals and Moving to an External DB
- 2026.06.01 Adding a Brain and an Engine Room to GAS: Vertex AI and Cloud Run
Stuffing JSON or Images into a Sheet Cell Will Break It
A Google Sheets cell caps at 50,000 characters. Put a photo or signature in a record row as a data URL and the save breaks. Keep only metadata in the cell.
A single cell holds at most 50,000 characters. Turn a photo or signature into a data URL (data:image/png;base64,...) and drop it into a record row, and one image alone runs tens of KB to a few MB: past the limit instantly. The save silently truncates or fails.
Why it matters
When a field-inspection app tries to store several photos plus a signature in one row and hits the wall, the whole record entered that day is lost. The error isn’t obvious, so the cause takes time to find. The cost is “a second trip to the site.”
The fix: separate the binary, keep only a path
Store metadata and a file ID in the record row. Keep the binary as a file in Drive and leave just the fileId in the cell.
// Bad: data URL straight into the record -> over 50,000 chars
record.photo = "data:image/jpeg;base64,/9j/4AAQ...";
// Good: file in Drive, only id + metadata in the cell
function savePhoto(base64, mime) {
const blob = Utilities.newBlob(Utilities.base64Decode(base64), mime, "photo.jpg");
const file = DriveApp.getFolderById(FOLDER_ID).createFile(blob);
return { fileId: file.getId(), bytes: blob.getBytes().length };
}
Keep the binary (data URL) as export-only state used when rendering on screen, and never put it in the storage model.
Two pitfalls that ride along
1) Formula injection. Input starting with =, +, -, or @ is read as a formula by Sheets. Prefix a ' before saving to neutralize it.
function escapeFormula(v) {
return /^[=+\-@]/.test(String(v)) ? "'" + v : v;
}
2) JSON serialization. JSON.stringify() does not safely restore Date, Map, Set, undefined, or NaN. Store dates as ISO strings, or declare an explicit replacer/reviver.
Deeper: Properties has the same trap
PropertiesService caps each value at 9KB. It’s a store for a cursor, small config, or an idempotency key: not a database. Any state that can exceed 9KB belongs in a Sheet, a Drive JSON blob, CacheService, or an external DB.
Big things live outside the cell; the cell holds only a pointer. Keep the binary in Drive and leave just the fileId and metadata in the record row.
Frequently asked questions
- What is the character limit for a Google Sheets cell, and what happens when it is exceeded?
- A single cell holds at most 50,000 characters. Exceeding it causes the save to silently truncate or fail, potentially losing the entire record entered that day.
- What is the correct way to store photos or signatures in a record row?
- Never put a data URL directly in a cell. Save the binary as a file in Drive and store only the fileId and metadata in the cell. Keep data URLs as export-only state.
- How do I prevent formula injection when saving user input to a Sheet?
- Input starting with =, +, -, or @ is interpreted as a formula by Sheets. Prefix a single quote (') before saving to treat it as plain text.
- 2026.07.29 What Breaks When Apps Script Projects Sprawl
- 2026.07.16 Why a Daily Apps Script Trigger Suddenly Stops
- 2026.07.15 What Breaks When Customer Data Lives in Browser Storage
- 2026.07.07 When the Browser Throws Your Data Away: The Green Checkmark Lies
- 2026.07.07 Installed ≠ Working: the Backup Trigger Never Ran Once
- 2026.07.07 GAS Has No Gradual Rollout: One 'Correct' Check Locks Out Every User
- 2026.07.07 Sheets Turns Your Text Into a Date: When '2026-06' Blocks Completion Forever
- 2026.06.18 An App Where Many Write to One Sheet: Concurrency, Duplicates, and Export Design
- 2026.06.12 Don't Read Cells One at a Time: Service Calls Are the Real Cost
- 2026.06.11 Stuffing JSON or Images into a Sheet Cell Will Break It You are here
- 2026.06.10 One Deploy Setting Decides Your Security Model: Execute as / Access
- 2026.06.09 The 6-Minute GAS Limit: Chunk and Resume Before It Kills You
- 2026.06.08 PropertiesService Is Not a Database: the 9KB Wall
- 2026.06.07 Why Saving a Screen as an Image Freezes on Mobile
- 2026.06.06 Concurrent Writes to the Same Sheet Break: LockService and the 30-Run Ceiling
- 2026.06.05 Your /exec URL Must Not Change on Every Redeploy
- 2026.06.04 External Calls Fail Sometimes: Exponential Backoff and a Retry Budget
- 2026.06.03 HtmlService Apps: Shell First, Then Async Load and Mobile Back
- 2026.06.02 When to Graduate from GAS: Limit Signals and Moving to an External DB
- 2026.06.01 Adding a Brain and an Engine Room to GAS: Vertex AI and Cloud Run