Troubleshooting
| Symptom | Cause and fix | |
|---|---|---|
| Anything at all, and you need to know what you're running | ./blog.sh version -- it needs neither env.sh nor a config, on purpose. | |
| The site looks wrong, or you want to change how it looks | ./style.sh -- a menu over everything that decides how the site looks and what it says about itself; it lists its own sections. The bio, the footer note, the copyright line and the banner's claim are Markdown, the same as a post, and raw HTML still works in them; a multi-paragraph value needs YAML's literal block (` | -) -- the notes in config/site.yml.example` show why. Picking a palette offers a preview (light and dark side by side, uploaded to the site too so a phone can see it) before anything is written -- temporary on purpose, the next build takes it down -- and the run ends by offering a rebuild. |
| Anything config-shaped, and you want the whole picture | ./blog.sh doctor -- it reads whatever is on disk and reports every problem at once, each with a fix line. It runs on a config too broken for anything else to load, including one whose YAML won't parse, and needs neither env.sh nor a valid config. Add --online to also ask whether the feeds, the analytics script and the access token still answer, and whether the deploy target does -- with what stands in its root that the site did not put there, and --strip-location to clean the location out of photos saved before the engine did it on the way in (Writing from a phone); rebuild afterwards, since the site is built from what it cleans. | |
| Anything archive-shaped -- a hole where a picture should be, a link that leads nowhere | ./blog.sh check -- it walks every post and every media file and says what is broken inside the archive, each finding with a fix line; --online additionally asks whether the links that leave the site still answer (Checking the archive). It only ever reports: nothing is deleted or rewritten, so it is safe to run at any time, including from cron. | |
config/site.yml is not valid YAML | The message names the line and column. Almost always a tab where spaces belong, a missing quote, or a colon inside an unquoted value (title: Colon: here). ./blog.sh doctor says the same thing without stopping at the first problem. | |
| A save aborted and took your text with it | It didn't: the text is in .last-edit.md, and the next add/edit offers it back -- [r] opens the editor on it, [d] throws it away, [c] leaves it alone. Text from an interrupted edit <slug> is only offered to that same post: restoring it into an add would make a second post out of it, so the offer names the command that does continue it. | |
Missing env.sh | ./setup.sh, or copy the template by hand: cp env.sh.example env.sh && chmod 600 env.sh. An unedited copy works locally. | |
Missing config/site.yml | Same two ways: ./setup.sh, or cp config/site.yml.example config/site.yml and fill it in -- the build refuses to guess. | |
Duplicate year/slug ... build stopped | Two posts resolve to the same URL and media directory. Rename one slug; the build aborts rather than silently overwriting one with the other. | |
| Deploy stopped with a "% drop/increase" message | One of the four guards (Deploying) -- broken build until proven otherwise, --force only when the change is intended. | |
| Deploy or save stopped naming an oversized file | The one file-size limit (Deploying) -- shrink the file, or take it out of the post and link to it instead. | |
N deploys in a row have not finished | Something is refused every time; the failures listed under that line say which (Deploying). | |
Surfer ... refused the access token (HTTP 401) | Surfer 7 no longer takes access tokens. Create an app password in your Cloudron profile (Profile -> App Passwords) and set SURFER_USERNAME and SURFER_PASSWORD in env.sh (Getting started). | |
Surfer ... refused the username and password (HTTP 401) | SURFER_USERNAME is the Cloudron username, not the e-mail, and SURFER_PASSWORD an app password, not the Cloudron sign-in password. A refused sign-in stops the deploy at the first file, and ./scripts/deploy-web.sh then exits with 4 rather than 1, so a script can tell "fix env.sh" from "try again". | |
Mastodon API returned 401 / toot was not created | MASTODON_ACCESS_TOKEN missing, expired, or lacking the write:statuses scope. The post itself is fine -- fix the token and use ./blog.sh toot <slug>. | |
Posting to Bluesky failed / announcement not sent | BLUESKY_APP_PASSWORD missing, revoked, or it's the account password instead of an app password (Settings → Privacy and security → App Passwords). The post itself is fine -- fix it and use ./blog.sh bluesky <slug>. | |
Every comment disappeared after turning on comments.approval | Expected until you star them -- moderation publishes only favourited replies (Cron). If starring changes nothing either, ./blog.sh doctor --online catches the usual cause: a token without read:statuses. | |
| A comment you starred still isn't on the site | It arrives with the next refresh-sidebar.sh run; an older post waits for the full pass, which --full does now (Cron). Check the cron job is actually installed. | |
comments.approval is on but MASTODON_ACCESS_TOKEN is not set | The cron run refused to publish rather than emptying every thread; the pages keep their last known comments until the token is there (Cron). Same for BLUESKY_APP_PASSWORD on a Bluesky site. | |
| Sidebar widget disappeared from the page | Its fetch returned nothing repeatedly (refresh-sidebar logs say which) -- the widget card hides when its JSON is empty/unreachable. Check the instance/feed URL in config/site.yml. | |
MISSING media: <slug> -> <file> during build | A post references a file that isn't in media.nosync/<year>/<slug>/ -- restore the file or edit the post. The build continues, and a copy already uploaded stays on the site rather than being pruned, so the page keeps working until you fix it. | |
Unreadable post file(s) ... build stopped | A post's JSON is truncated or isn't a post object -- the message names every offending file. Fix or remove them; list and the pickers keep working meanwhile and name it too. | |
The image size could not be read when attaching a photo | PNG, JPEG, GIF and WebP are measured; anything else is attached and rendered without reserved space, so the page jumps once while loading. | |
HEIC displays only in Safari when attaching a photo | The iPhone default format. Convert it with the command the message prints, set media.convert_heic: true to have the engine do it, or set the phone to Settings → Camera → Formats → Most Compatible. | |
| A post sent from the phone came back refused | The answer names a code. bad_name, too_large, truncated, empty_input, empty_file and bad_base64 are about the delivery -- send it again, and see A post sent from the phone itself for the ceiling and the closing dot. bad_reference means the markdown named a picture by a path rather than a bare filename. bad_markdown is markdown no blocks can be made of -- a picture inside a sentence, a video with no caption -- and the message says where; unknown_frontmatter_key names a header line the engine does not know, not_text a file that is not UTF-8 text, empty one with nothing under its header. A file is read as the post when its name ends in .md (or .md.txt); under any other name, .markdown included, it is kept as an attachment. missing_images means the text arrived before a picture it names: send it again, the pictures already there are found. | |
name_taken from the receiver | Something in incoming/ under that name is not a plain file -- a directory, or a symlink. Nothing was replaced; clear the name on the server. | |
busy from the receiver | Another delivery was still being taken in when this one had waited as long as it waits (BLOGSH_TURN_SECONDS, 300). Nothing of this one was stored; send it again. | |
write_failed, no_incoming, no_engine, no_tmp, no_cd | The installation, not the delivery: the path in the shortcut's command is wrong, incoming/ is missing, or the account behind the key cannot write into it. Every one of them answers with its code and leaves with a zero status, the same as any other refusal: the phone discards the output of a command that failed, and these are the answers a first delivery to a new install most needs to read. | |
/markdown/ page missing | templates/markdown-cheat-sheet.<lang>.md was removed -- restore it from the repo (git checkout templates/). | |
| A published post shows the wrong date | Publishing uses "now" and scheduling uses the date you entered, so a surprising date means a date: line was typed into the frontmatter by hand -- it's respected, including past dates (which skip the homepage -- by design). | |
| sftp deploy hangs | It's waiting for a password -- the sftp backend needs key-based auth (see Getting started). |
When in doubt: ruby build/build_blog.rb and ./scripts/deploy-web.sh --dry-run are both safe to run any time -- the build only writes into public.nosync/, and a dry run touches nothing at all.
