bench migrate fails on is_standard: 1 Fixtures in Production (CI/CD)

Hello Community,

I am building a custom app and using Fixtures to sync critical business logic records (e.g., specific Notifications, Workflow States) that must be protected from user modification.

The Setup:

  • We export these records as fixtures with is_standard: 1 to prevent users/System Managers from deleting them in the UI.

  • We use a CI/CD pipeline. When we push code, the Production server pulls the Docker image and runs bench migrate.

The Problem:
When we modify a fixture in the code (e.g., change the email subject) and deploy, bench migrate crashes with:

ValidationError: Cannot edit Standard Notification. To edit, please disable this and duplicate it.

The Question:
How can we maintain is_standard: 1 (to protect data integrity from users) while still allowing bench migrate to update these records via code deployments?

Is there a specific flag or hook we need to set so that bench migrate is treated as a privileged “Installer” that ignores the Standard validation check?

Current Workaround:
We are forced to set is_standard: 0 in our fixtures to make CI/CD pass, but this leaves the records vulnerable to user deletion.

Thanks in advance!

1 Like

Hello @Chandru03 ,

This is expected behavior in Frappe, and you’ve already identified the core issue correctly.

Why this happens

Records with is_standard = 1 are treated as immutable by design.
Frappe enforces this rule not only in the UI, but also during bench migrate, fixtures import, and data sync.

So when a fixture changes (for example, subject or message content of a Notification), Frappe blocks the update with:

Cannot edit Standard Notification

This is intentional. There is no special “installer” or “CI mode” that bypasses this validation.


Important clarification

  • bench migrate is not privileged beyond normal server-side permissions

  • Fixtures are imported using standard ORM logic

  • Standard records are expected to be code-owned and frozen, not versioned like configuration


Recommended approaches (used in real projects)

Option 1 — Use is_standard = 0 + permission locking (recommended)

Instead of relying on is_standard:

  • Keep is_standard = 0

  • Lock records using:

    • Custom permissions (remove delete/write from System Manager)

    • Property Setter to disable delete

    • Custom validation hook (before_delete, before_save)

This allows:

  • CI/CD updates via fixtures

  • Protection from accidental UI edits

  • Clean migrations

This is the most practical and flexible solution.


Option 2 — Split immutable “definition” from editable “instance”

For things like Notifications or Workflows:

  • Keep a standard template (code-defined)

  • Generate or update runtime records programmatically in:

    • after_migrate

    • after_install

    • a custom patch

In this model:

  • The template is code-owned

  • The actual Notification is regenerated if missing or outdated

This avoids fixture conflicts entirely.


Option 3 — Patch-based updates (advanced)

Instead of modifying fixtures:

  • Leave the fixture untouched

  • Apply changes via a patch (patches.txt)

Example use case:

  • Update subject/message only if it differs

  • Run once per deployment

This works but increases maintenance overhead.


What is not supported

  • No flag to tell Frappe “ignore is_standard during migrate”

  • No hook to elevate fixture imports

  • No safe way to mutate standard records via fixtures

Any attempt to bypass this usually breaks upgrade safety.


Summary

  • is_standard = 1 means read-only forever

  • Frappe enforces this consistently (by design)

  • CI/CD-friendly setups should not rely on is_standard

  • Use permissions, hooks, or regeneration logic instead

Your current workaround is common — the next step is to replace is_standard with controlled protection, not immutability.