Things that cost me a day, so they cost you none
Working notes from building Invision Community and Flarum applications. Mostly the failures that give no error at all — the ones where everything installs cleanly and quietly does the wrong thing.
Invision Community 5
86 articlesExtensions and contracts
39What each extension point is for, what it must declare, and what happens when it is wrong — which is usually nothing visible.
- A content listener without $class takes down your whole community
- Content behaviours are TRAITS, so instanceof is always false
- Event listeners: all 58 hooks, and the argument-order trap
- Extension method signatures are fixed, and a mismatch is a fatal on installNew
- MemberFilter extensions are gated by a hardcoded area whitelist
- One content listener for every content type, including Pages databasesNew
- The core/AccountSettings extension in Invision Community 5
- The core/AchievementAction extension in Invision Community 5
- The core/AdminNotifications extension in Invision Community 5
- The core/Build extension in Invision Community 5
- The core/CommunityEnhancements extension in Invision Community 5
- The core/ContactUs extension in Invision Community 5
- The core/ContentRouter extension in Invision Community 5
- The core/Dashboard extension: ACP dashboard blocks in Invision Community 5
- The core/EditorMedia extension in Invision Community 5
- The core/FrontNavigation extension in Invision Community 5
- The core/GroupForm extension: adding fields to the member group form
- The core/GroupLimits extension: merging secondary group settings in Invision Community 5
- The core/IpAddresses extension in Invision Community 5
- The core/LiveSearch extension in Invision Community 5
- The core/MemberACPProfileBlocks extension in Invision Community 5
- The core/MemberACPProfileTabs extension: AdminCP member view tabs in Invision Community 5
- The core/MemberExportPersonalInformation extension in Invision Community 5
- The core/MemberHistory extension in Invision Community 5
- The core/MemberRestrictions extension in Invision Community 5
- The core/MetaData extension in Invision Community 5
- The core/MFAArea extension in Invision Community 5
- The core/ModCp extension in Invision Community 5
- The core/ModCpMemberManagement extension: adding tabs to the ModCP member management page in Invision Community 5
- The core/ModeratorPermissions extension in Invision Community 5
- The core/Notifications extension in Invision Community 5
- The core/OutputPlugins extension: how a {tag} in a template is resolved
- The core/OverviewStatistics extension in Invision Community 5
- The core/Permissions extension and node permission integration in Invision Community 5
- The core/ProfileSteps extension in Invision Community 5
- The core/Sitemap extension in Invision Community 5
- The core/Statistics extension in Invision Community 5
- The core/Uninstall extension in Invision Community 5
- Where OAuth account links live, and why you should not keep your own copyNew
Languages and text
5The string table, translation, and the places where text does not appear where you expected it to.
- A core/Notifications extension needs a language key named after the CLASS
- A module shows as module__myapp_thing on the administrator restrictions screen
- addToStack() often returns a placeholder, not translated text
- Creating a language in code: set_short() does not store the locale
- Why your app shows menutab__content or module__myapp_thing to customersNew
Theming, templates and forms
9Theme hooks, CSS that survives both colour schemes, and building forms that do not throw on render.
- "This theme may be out of date" usually means your form field, not the theme
- A container query cannot style its own container
- core/Loader js() and css() must return an array of arrays
- Custom CSS that works in both colour schemes, and the var() name trap
- Form\Editor needs an EditorLocations extension that actually exists
- Setting a theme custom_css and calling save() silently does nothing
- The Invision Community 4 CSS class that silently does nothing in 5New
- Theme hook types are an enum of four, there is no "replace", and how to change the page title anyway
- Widget conventions: two language keys, ipsWidget markup, and cached blanks
Background work and scheduled tasks
5The queue system, work that has to happen after the response, and jobs that finish without doing anything.
- A realtime gateway will let any member join any channel unless you sign the channel into the tokenNew
- Running code on every request, after the page has been sent
- The core/Queue extension in Invision Community 5
- The report that shows nothing: only_full_group_by, and why catching the exception is the real bug
- Your background task is running inside a visitor's page loadNew
Data, settings and storage
11The database layer, settings, tags, file storage, and backing up a live site.
- A Pages category created in code sends every article into a redirect loop
- Building a podcast feed Apple will acceptNew
- Creating a Pages database and categories in code
- Db::insert() third and fourth arguments do very different things
- Dumping an Invision Community database from PHP: ANSI_QUOTES, multi-member gzip, and the 0x trap
- Pages page content is not a template: why template syntax prints itself, and why removing an element does not stick
- S3 and R2 storage break in any CLI script without HTTPS=on
- Taggable::setTags() deletes every existing tag first
- The ?: fallback silently breaks any setting an admin can set to zero
- The core/FileStorage extension in Invision Community 5
- There is no event when a tag is created, and what that means for your app
AI features and expectations
5What these features do, what they cost, and what buyers reasonably but wrongly assume they do.
- A chat model in an embeddings field fails silently, and looks like a broken app
- Why a similarity threshold is never enough for automatic classification
- Why your AI assistant's links arrive as plain textNew
- Your AI assistant is only as good as what you gave it to read
- Your classifier does not learn, and everyone will assume it does
Application structure and releases
11The JSON files an application is made of, versioning and upgrade steps, and testing from the command line.
- "You do not have permission for that" on your own AdminCP screen: the $csrfProtected property
- A malformed manifest silently disables your whole application
- A shared image vanishes the moment one member changes their profile photoNew
- A stray "menutab_system" in your AdminCP: acpmenu.json tabs are not validated
- Application versions: why re-uploading the same version does nothing
- Invision Community already runs an OAuth 2.1 server — do not write another oneNew
- Raw language keys in the AdminCP menu: acpmenu.json must be an object
- Six ways an Invision Community 5 application fails silentlyNew
- What you can and cannot test from the command line
- Your editor toolbar button never appears, and nothing is loggedNew
- Your friendly URLs have the app name in them twiceNew
Realtime, chat and calls
1WebSocket gateways, relays and the server-side pieces live features depend on — where "it works when I test it" and "it works for your members" are different claims.
Flarum 2
6 articles- Flarum 2: AbstractModel mass assignment throws a 500
- Flarum 2: frontend model relations must use the hasOne/hasMany call pattern
- Flarum 2: json-api-server returns 403 for declared but unwritable fields
- Flarum 2: Laravel facades throw "facade root has not been set"
- Flarum 2: never redefine core CSS variables at :root
- Flarum 2: notification Blueprints need getFromUser()
Nothing matches that.
Application structure and releases
11 Articles in this category
-
If you are building anything that needs a third party to act on a member's behalf — an AI connector, a mobile app, a desktop client, an integration a customer authorises — the instinct is to invent a token scheme. A long secret in a URL, an API key in a settings field, a bearer token you mint yourself. Do not. Invision Community 5 ships a complete OAuth 2.1 authorization server, and almost nobody building on the platform seems to know it is there. It is not a stub. It supports PKCE, refresh tokAdded by Ernest Defoe
- 0 comments
- 2 views
-
These six were all found in a single application, over a single week, and they have one thing in common: none of them produces an error that points at the mistake. Four produce no error at all. One produces a blank 500 whose stack trace blames the wrong line. One leaves a page that still looks styled while a rule you wrote is quietly missing. They are ordered by how hard they are to notice, hardest first. If you are auditing an existing application rather than reading straight through, the lasAdded by Ernest Defoe
- 0 comments
- 4 views
-
Your application offers members a set of images to use as their profile photo — avatars you supply, badges, team crests, anything shared. It works. Then one member picks a different one, and the image disappears for everyone else who chose it. No error, no log entry; just a broken avatar on other people's posts, discovered days later. This is not a bug in your code so much as a consequence of how Invision Community stores profile photos, and it is easy to walk into because the obvious implemenAdded by Ernest Defoe
- 0 comments
- 8 views
-
Invision Community 5's rich-text editor is TipTap, shipped as a compiled bundle. There is no source and no npm package, but there is a real third-party plugin API — and almost every way of getting it wrong fails silently. The editor loads perfectly, your button is simply absent, and there is nothing in any log to tell you why. How an application ships editor JavaScript Put plain .js files in applications/<app>/dev/editor/. Building the application writes them into data/editor.xml, and aAdded by Ernest Defoe
- 0 comments
- 16 views
-
You define a friendly URL, load the page, and the address reads /discord/discord/invite/abc123. The route works, nothing errors, and the duplication looks like a typo somewhere you cannot find. It is not a typo. Invision Community prepends topLevel to every friendly path in the file. Repeat it in the path and you get it twice. Wrong { "topLevel": "discord", "pages": { "myapp_invite": { "friendly": "discord/invite/{@key}", "real": "app=myapp&modul
-
If your application's menu entry shows as menu__yourapp_settings_0 instead of its name, the shape of data/acpmenu.json is wrong — not your language file. Wrong, and it looks perfectly reasonable: { "settings": [ { "tab": "community", "controller": "settings" } ] } Right: { "settings": { "settings": { "tab": "community", "controller": "settings" } } } Why the trailing number appears The structure is { module: { item: { … } } }, and the item key becomes part of the language key — menu__&
-
You install an application and a tab appears in the AdminCP navigation called: menutab_system sitting between Community and Members, looking like a corrupted install. Nothing is logged, nothing errors, and the application otherwise works perfectly. The cause The tab value in data/acpmenu.json is never validated. Invision Community takes whatever you write, creates a tab for it, and looks up its display name from the language key menutab__<tab> — note the double underscore. If no such k
-
You add an AdminCP controller. The list screen works. Then you click Add, or Edit, or a date-range link on your own report, and Invision Community answers: Sorry, you do not have permission for that! You are logged in as a root administrator. Nothing is wrong with your ACP restrictions, and that is exactly where the message sends you to look. The cause is CSRF, not permissions IPS\Dispatcher\Admin runs this before dispatching: if ( !isset( $this->classname::$csrfProtected ) and arra
-
An application whose listeners.json or extensions.json does not parse installs perfectly, shows its AdminCP screens, saves its settings — and never runs a single line of its own automation. There is no error, no log entry, and nothing in the interface that looks wrong. The cause Core reads both manifests the same way, inside an and chain, with the warning suppressed: if ( file_exists( $jsonFile ) and $json = @json_decode( file_get_contents( $jsonFile ), TRUE ) ) { foreach( $json as $file
-
An application's versions live in data/versions.json, keyed by a long integer version and valued with the human one: { "10000": "1.0.0", "10001": "1.0.1", "10002": "1.0.2" } Uploading the same version number is silently a no-op Invision Community compares the long version in the uploaded file against the installed one. If it is not higher, the upload is accepted and nothing happens. No error, no warning, no changed files. This is the single most wasteful mistake in application de
-
Loading init.php from a CLI script gives you most of the framework, and it is by far the fastest way to test an application. Some things genuinely cannot run there, and knowing which saves a lot of time spent debugging the harness instead of the code. The basic harness <?php require_once '/var/www/html/init.php'; use IPS\Db; /* IPS installs its own error handlers. Under CLI a fatal can end the process with NO OUTPUT AT ALL, which reads as the script silently stopping. Write to a fi