File uploads
How gform fills Google Forms file upload questions — YAML sources, auto-apply vs assist, Drive chips, mime/size gates, artifacts, and decision logs.
Related: schema · commands/run · artifacts · limitations · agents
Docs: thegform.pages.dev/file-uploads
Mental model
A file step does one of two things:
- Configured source → gform resolves bytes, checks mime/size, tries Playwright
setInputFiles, copies intoartifacts/<id>/content/, statusuploaded. - No source (assist-only) → headed + TTY pause; you upload in the browser; status
assisted(orskippedif nothing attached). Headless without a source fails withassist-headed-required.
gform does not drive the Google Drive picker UI. You either point YAML at bytes (local / gdrive / remote) or upload yourself under assist.
Prefer source: (three types)
- action: file
question: Resume
source:
type: local # or gdrive | remote
path: ./files/resume.pdf
accept: [pdf]
maxBytes: 10485760 # optional size gate (bytes)source.type | Required fields | What run does |
|---|---|---|
local | path: | Read from disk (cwd-relative or absolute) |
gdrive | id: and/or link: | Parse Drive file id → download with the auth session (--auth) → cache → attach |
remote | url: (https) | Fetch once into $GFORM_HOME/cache/files → attach |
gdrive: support both id and link
Use whichever you have. If both are set, id wins. If only link is set, gform parses the file id.
# Explicit id (from Forms chip data-id, or share URL path)
source:
type: gdrive
id: 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
# Or a share / open link — id is parsed
source:
type: gdrive
link: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/view?usp=sharing
# Both ok — id used; link kept for humans
source:
type: gdrive
id: 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
link: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/viewParsed link shapes (non-exhaustive):
| Link shape | Parsed? |
|---|---|
drive.google.com/file/d/<ID>/… | yes |
drive.google.com/open?id=<ID> | yes |
drive.google.com/uc?id=<ID>&export=download | yes |
aistudio.google.com/…?state={"ids":["<ID>"],…}&usp=drive_link | yes — Drive Share → Copy link often opens AI Studio with the id in state |
Bare id (A-Za-z0-9_-, length ≥ 10) | yes (as id:) |
| `docs.google.com/document | spreadsheets |
Prefer a bare id: when you can (most reliable). gdrive fetch needs a live browser + Google cookies → run with --auth (or an active profile). Offline gform verify does not download Drive bytes (tip points at gform run … --auth). Sync live Forms accept categories with gform verify <form> --check (add -A when the form needs auth).
Assist-only
- action: file
question: Document
# no source
accept: [pdf] # optional; still used if you later add a sourceRequires --headed (and a TTY). Headless → hard fail assist-headed-required (not a fake success).
Decision map (what run logs)
Every auto choice is a content · decide · … notice (operators + agents). Flow:
file step
├─ has source / pool|urls?
│ ├─ no → decide: assist-only → headed wait OR headless fail
│ └─ yes → decide: apply source.type=local|gdrive|remote
│ ├─ resolve bytes (disk / cache / Drive fetch)
│ ├─ decide: mime ok|fail (accept YAML ∪ live Forms)
│ ├─ decide: size ok|fail (if maxBytes set)
│ ├─ try attach (Add File → Drive picker iframe setInputFiles / filechooser)
│ │ ├─ Selected-files chip → decide: uploaded · status=uploaded · copy content/
│ │ └─ no chip → decide: assist-fallback (headed) OR fail
│ └─ resolve error → decide: assist-fallback (headed) OR fail
└─ after assist Enter
├─ Selected-files chip present → status=assisted (+ best-effort Drive fetch)
└─ empty → status=skipped (not success for --once / unique)| Decide tag | Meaning |
|---|---|
apply | About to resolve this source.type |
mime-ok / mime-fail | Accept gate |
size-ok / size-fail | maxBytes gate |
uploaded | setInputFiles succeeded |
assist-only | No configured source |
assist-fallback | Auto path failed; headed assist next |
With -pa / --prefer-ask (headed + TTY), mime / size / fetch fails become a rematch menu: another local path, Drive id|link, remote url, raise maxBytes, assist, skip, or abort — instead of silent apply-or-fail. Headless → warn and ignore -pa.
Accept + maxBytes
| Field | Role |
|---|---|
accept: [pdf, image, .png, pptx, …] | Overrides live Forms categories when set; bare exts (pptx) and dotted (.pptx) both work; mime-checked on verify / edit save / run |
maxBytes: N | Reject after resolve if file is larger (remote + gdrive + local) |
gform verify and edit --ask Save use the same gates (gdrive download deferred until run). After the form owner changes allowed types, run gform verify <form> --check -A to widen YAML accept and fill missing maxBytes / maxFiles from hydrated live hints (Upload N … Max N MB is per file; form-wide Drive quota is separate).
Pools / variants
| Field | Effect |
|---|---|
pool: [./a.pdf, ./b.pdf] | Per-run local path → resolved as source.type=local |
urls: [https://…, …] | Per-run remote URL → source.type=remote |
Templates in source.path / source.url / source.id / source.link | Same {{i}} / {{run}} rules as other steps |
options.variantMode: rotate · random · zip — see repeat.
Drive chip detect (assist / page already has a file)
When the Forms Selected files list has a listitem (jsname="XPtOyb", data-id, filename):
- Empty list + visible Add file → nothing attached
- Listitem present → gform records the chip; may fetch Drive bytes with the auth session into
artifacts/<id>/content/ - That is detect + download, not auto-picking from My Drive
Do not confuse with YAML source.type: gdrive, which you configure up front.
Artifacts
| Path | When |
|---|---|
artifacts/<id>/content/ | Bytes copied after successful resolve + attach (or Drive chip fetch) |
manifest.content[] | Per-question: source, mime, sha256, bytes, status |
Statuses: uploaded · assisted · skipped. Failed assists are not success — --once / -sd / unique stay clean.
gform run view <id> prints a Content section when present.
Headed vs headless cheat sheet
| Situation | Headed + TTY | Headless |
|---|---|---|
source applies + settable input | auto upload | auto upload |
source applies but Drive-only UI | assist pause | fail assist-headed-required |
| no source | assist pause | fail assist-headed-required |
| soft wall (CAPTCHA, …) | assist | fail (never pauses) |
Use --headed -ko while teaching uploads. --auth for login-walled forms and gdrive downloads.
Headless uploads can still fail when Google does not expose a settable input — that is an external UI limit, not a silent success. Prefer headed assist while diagnosing.
Accept categories
Tokens may be categories, MIME types, or extensions (pptx / .pptx):
| Category aliases (examples) | Covers |
|---|---|
pdf | application/pdf |
image / video / audio | matching MIME families |
document | Word / ODT / text / rtf / pdf, … |
spreadsheet | Excel / ODS / csv, … |
presentation | PowerPoint / ODP, … |
drawing | image/ + pdf |
YAML accept overrides live Forms categories when set; run/verify still mime-check resolved bytes. Live hints sync via gform verify <form> --check (add --auth when needed).
Concurrency and picker risk
Authenticated file uploads under high --concurrency are a known stress point:
- Multiple workers can race on picker iframes / newly created inputs
- Google Drive cache hits are helpful but must stay concurrency-safe (hardening on the roadmap)
- Recommended while debugging: concurrency 1,
--headedif assist may be required,--pw-traceon failures - Do not load-test third-party forms — use forms you own
gform does not claim flawless file uploads. Failed attaches should leave decide logs, manifests, and screenshots when possible.
Debugging uploads
gform run <form> --auth --policy demo
gform run <form> -r 3 -C 1 --auth -d --pw-trace
gform run last
gform run view <id> # Content section + status uploaded|assisted|skippedInit / edit
gform initstubs file questions with commentedsource:examples.gform edit <form> --ask→ Edit source (local | gdrive | remote | assist) + accept + maxBytes.- Prefer asking humans whether they want auto-upload vs assist before inventing a path.
Related
- schema — full YAML contract
- commands/run — flags + assist
- commands/verify — content notices
- artifacts — layout
- limitations — walls
- roadmap — picker / concurrency hardening (planned)
- auth — profiles for Drive fetch