Add two posts: headless-browser scraping limits + NocoDB backfill gotchas
Deploy / build (push) Successful in 1m12s

- why-your-headless-browser-cant-scrape-everything (case study, en+zh)
- the-nocodb-attachment-that-wouldnt-update (note, en+zh)
- og + banner images for both, generator entries added
- gitignore .og/ scratch dir (also removes previously-committed scratch files)
This commit is contained in:
hoelee committed 2026-09-09 06:56:20 +08:00
1 parent ca88623486
commit 62b681bad1
46 files changed
+537 -1299

No files matched your search

@@ -0,0 +1,93 @@
---
title: "The NocoDB Attachment That Wouldn't Update (and a Regex That Missed)"
description: "Two small gotchas from backfilling 300 rows: NocoDB ignores a path change when the attachment keeps its id, and a lookahead regex silently skipped URLs without a file extension."
pubDate: 2026-09-10
category: notes
tags: [nocodb, backfill, python, regex, data-pipeline]
ogImage: /og/the-nocodb-attachment-that-wouldnt-update.png
banner: /banners/the-nocodb-attachment-that-wouldnt-update.png
---
Backfilling 300 rows in NocoDB surfaced two small gotchas that each ate a chunk of
time. Both are the kind of thing that only bites you when the data you're fixing
is *almost* — but not quite — in the shape the code assumes.
## Gotcha 1: updating an Attachment's `path` doesn't matter if you keep the `id`
I wanted to swap every thumbnail URL in an Attachment column for its full-size
version. The obvious move is to edit the JSON the column stores:
```python
img = json.loads(row["image"]) # [{"id": "...", "path": "...", "signedPath": "..."}]
img[0]["path"] = new_fullsize_url
img[0]["signedPath"] = new_fullsize_url
```
It looked like it worked — the PATCH returned success. But reading the rows back,
every `path` was still the old thumbnail.
The culprit: the attachment carries an NocoDB-generated `id`, and as long as it's
present, NocoDB resolves the attachment *by that id* and happily ignores the new
`path`. The field I thought I was updating was effectively read-only while the `id`
was still there.
The fix is to drop the server-assigned fields and let NocoDB re-register the
attachment from scratch:
```python
img[0] = {
"path": new_fullsize_url,
"mimetype": "image/jpeg",
"title": img[0].get("title", "image.jpg"),
}
# no "id", no "signedPath" — NocoDB assigns fresh ones
```
With `id` gone, NocoDB registers a new attachment from the `path` and generates a
fresh signed URL. The value finally sticks.
**Rule:** if a NocoDB Attachment edit isn't sticking, check for the `id` field.
Strip it and re-register instead of patching in place.
## Gotcha 2: a lookahead regex that silently skipped extension-less URLs
To build the full-size URL I needed to strip the `_progressive_thumbnail` suffix.
My first pass used a lookahead:
```python
clean = re.sub(r"_progressive_thumbnail(?=\.\w+$)", "", url)
```
That works for:
```
...70776f54_progressive_thumbnail.jpg → ...70776f54.jpg ✅
```
But Carousell serves *two* URL shapes. Some thumbnails end with the bare suffix
and **no extension**:
```
...f94af259_progressive_thumbnail → (lookahead doesn't match) ❌
```
Because the lookahead requires `.\w+` at the end, the second shape slipped through
unmatched — 25 of my 300 rows. The regex "worked," it just silently didn't do
anything on that slice of the data.
The robust version is a plain replacement, which handles both:
```python
clean = url.replace("_progressive_thumbnail", "")
```
**Rule:** when normalizing a string that has multiple real-world shapes, prefer
`replace()` over a regex unless you've enumerated every shape. A regex that
silently skips is worse than one that fails loudly.
---
Nothing here is exotic. But both are the *boring* kind of bug that only reveals
itself in real data — the attachment the API swears it updated, the regex that
matches 91% of the rows and says nothing about the other 9%. If backfilling data
into NocoDB ever feels like it's half-working, these two are where I'd look first.