01 · 2026

Open source

Around 145 merged fixes in Frappe and ERPNext, the framework a lot of businesses run their accounting on.

PythonJavaScriptFrappeERPNextMariaDB

Frappe is the framework ERPNext runs on, so a bug in it is a bug in the accounting software of everyone who installed ERPNext. Since December 2025 I have had around 145 patches merged: mostly Frappe itself, plus ERPNext, Print Designer, frappe-ui, Helpdesk, and CRM.

Most of it is core work: permissions, child tables, query building, timezone handling, caching. Below is what I would point at.

Permission leaks

The failures worth catching are the ones that fail open. A report with more than one link field returned rows the user was not allowed to see, because the permission check only covered the first link. Field-level data masking ignored role permissions on child table fields, so masked values stayed readable through a child row.

Two more came from one root cause: has_perm and the non-read permission checks both returned wrong answers when called before a doctype’s meta had loaded, which made the result depend on what else the request had touched first. In ERPNext, Update Items demanded cancel and delete rights just to remove a line from a document.

Child tables and grids

Rows in a child table carry identity, and identity is what breaks. Reordering rows and then syncing corrupted which row was which, so edits landed on the wrong record. Getting that right meant following how the client tracks rows across a save round-trip, rather than patching the symptom where it surfaced.

Time and the database

Frappe compares timestamps in SQL, which only works if the database and the application agree on what time it is. They did not. The database session now takes its time zone from System Settings on connect, and creation-based cutoffs in queries are evaluated against that same clock instead of the server’s.

Performance

A kanban board leaked memory and eventually hung after enough re-renders, which on a board people leave open all day means by afternoon. Workspaces rebuilt their allowed pages and reports on every load because an empty result was never cached. The monitor flush left one entry behind in Redis each cycle, then duplicated it on the next.

Features

A few things were missing rather than broken. Numeric fields now support min_value and max_value. Data import writes its failures to the log files instead of swallowing them. Changing a doctype’s module warns about the files left behind in the old one.

How the work goes

Almost every fix starts as a support ticket, and a ticket is never a description of the bug; it is a description of what someone saw. Closing that gap is the job, and I have written about it separately.

The rest is unglamorous. Reproduce it on a local bench, find the line, make the smallest change that fixes it without moving anything else. Framework code is load-bearing, and a tidy refactor sitting next to a fix is how one person’s bug becomes everyone’s bug. The diff stays boring on purpose; the reasoning goes in the PR description.