Dude, where’s my m2m field?

When you create a many-to-many relationship through sys_m2m, the platform generates the field names in the new table based on the names of the tables you’re connecting.

Most of the time this works fine. But there’s a specific scenario where it fails silently and you might not find out until you deploy to another instance.

How ServiceNow names m2m fields

When you create an m2m relationship, ServiceNow generates reference field names using the table names. The pattern is simple: take the table name and add a u_ prefix for custom tables.

Pre-populated values before creation:

After creation:

If you connect the User table to the Incident table, you get:

  • sys_user (no prefix added)
  • u_incident (prefix added)

ServiceNow skips the u_ prefix for tables that start with sys_. This becomes a problem when your app is in the global scope.

Looking good during development

Here’s where it gets weird. In your development instance, everything works perfectly. You can write scripts that reference the sys_user field. The script executes without errors.

You test your business rules, your flows, your UI actions and everything’s working fine.

You might notice some weird things though, but they are easy to go unnoticed.

The field is not automatically added to the List and Form views.

However, once added, it’s business as usual.

A more subtle one: if you check the dictionary for your m2m table, the Users field is there, but you don’t see the red X on the left to remove it.

The bug surfaces during deployment

Now, connect your application to GitHub and deploy it to another instance.

The import succeeds with a Warning and the field creation is skipped.

And… your m2m relationship is broken!

The sys_user field is missing in the target instance.

And every script that references it won’t work.

Background script failing in target instance with empty output when accessing sys_user field

Why this happens

ServiceNow enforces strict naming rules for fields in global scope. Custom fields must use the u_ prefix. The sys_ prefix is reserved exclusively for platform tables and fields.

When you create an m2m relationship locally, ServiceNow generates a field called sys_user that violates this rule.

Your development instance tolerates it somehow. But when you commit your application to source control and deploy it elsewhere, the field can’t be created because it breaks the naming convention.

The platform should automatically generate the field name as u_sys_user. It doesn’t.

The fix

Before you create the m2m relationship, manually rename the problematic field from sys_xxx to u_sys_xxx. This makes it compliant with global scope naming requirements and ensures it keeps working after deployment.

Or use scoped applications when possible instead of apps in the global scope. Scoped apps don’t have the same field naming restrictions and won’t hit this issue.

If you’re already in the deployment phase, I don’t have a good solution. When we faced this issue, we decided to recreate the m2m table with better names to avoid other unexpected issues somewhere down the road.


Tested in a PDI in Zurich