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.
Theming, templates and forms
9 Articles in this category
-
A class name that no longer exists is not an error. It applies no styling, logs nothing, and breaks no build. The element simply renders unstyled, and because the surrounding markup is right, it is easy to look straight past. This is how a panel shipped with its text flush against the left edge of its own box. The markup asked for ipsPad, which is an Invision Community 4 class. In 5 it does not exist, so there was no padding at all — and nothing anywhere said so. Check before you trust a cla
-
Theme hook types are an enum of four, there is no "replace", and how to change the page title anyway
You want to change what a theme outputs at a specific point. The obvious approach is a custom template hook with type replace. Install the application and the log says: Data truncated for column 'template_hookpoint_type' at row 1 The application installs, the hook does not, and nothing on the front end changes. The four types The column is an enum: template_hookpoint_type enum('before','inside-start','inside-end','after') That is the whole list. The string 'replace' does appear in core's o -
You add a field to an AdminCP form, load the page, and get this: [[Template core/global/forms/checkboxset is throwing an error. This theme may be out of date. Run the support tool in the AdminCP to restore the default theme.]] The theme is fine. Restoring it will not help. The message is what Invision Community prints when a template throws, and the template threw because of the options you passed to the field — but the message names the template, so it sends you to the support tool and the
-
Invision Community 5 renders in a light or a dark scheme. Custom CSS written while looking at one of them usually breaks in the other, and the way it breaks is quiet: nothing errors, nothing logs, and the developer's own screen looks correct. How the two schemes work Core declares its palette twice: light values at :root, then overrides under [data-ips-scheme="dark"]. The attribute is written onto <html> from front/global/htmlDataAttributes.phtml and can be light, dark, or system. When
-
Container queries let a block lay itself out based on the space it was given rather than the size of the browser window — the only way to make one block correct in both a narrow sidebar and a wide content column. A media query cannot tell those apart, because the viewport is the same width in both. The trap An element that declares container-type cannot be styled by queries against that container. Only its descendants can. /* BROKEN: the grid rule never applies */ .block { container-type:
-
A form containing an editor throws the moment the page is opened, not on submit, if its app and key do not name a real extension. The symptom OutOfBoundsException: Admin #0 /applications/myapp/modules/admin/.../controller.php(340): IPS\Helpers\Form\Editor->__construct(...) The exception message is the key alone, with no mention of the app, which makes it read like a missing language string. The cause new Editor( 'body', '', TRUE, array( 'app' => 'myapp', 'key' => 'Admin' ) ) This re
-
An application that registers a core/Loader extension and returns a flat list of URLs will take down every front-end page on the site, not just its own. The symptom Every front page returns a 500. The log says: TypeError: array_merge(): Argument #2 must be of type array, string given #0 /system/Dispatcher/Front.php(188): array_merge(Array, 'https://...') Note where the error comes from: the dispatcher, not your application. Nothing in the trace names the app responsible. The cause The front
-
Writing custom CSS to a theme in code appears to work. The setter runs, caches clear, save() reports success — and the column is never touched. The cause ActiveRecord::save() writes only fields recorded as changed: $data = $this->_new ? $this->_data : $this->changed; But Theme::set_custom_css() assigns straight into _data and never marks the field: public function set_custom_css( string $value ) : void { $value = str_replace( '</style>', '', $value ); $this->_data['
-
Every widget needs TWO language keys block_<key> and block_<key>_desc. Without them the AdminCP block picker lists raw key strings. Easy to ship without noticing, because the block itself renders fine. Use ipsWidget markup, never your own ipsBox The widget framework already supplies the box. Emitting ipsBox yourself nests a box inside a box and the header stops matching every other block on the page. <div class="ipsWidget ipsWidget--vertical"> <h3 class="ipsWidget__h