Thursday, June 19, 2025

9 Ways to Break (and Fix) Field Auditing in ServiceNow

Introduction

Field auditing in ServiceNow is essential for compliance, troubleshooting, and change tracking — especially in regulated industries or IRM modules. But here's the kicker: it's surprisingly easy to break audit logging without even realizing it, and there are more ways to do it than most guides on this topic cover.

This article walks through nine ways audit logging can fail — some through misused code, some through platform behavior working exactly as designed but surprising people who don't know about it — and exactly how to fix or prevent each one.

1. Using setWorkflow(false) or autoSysFields(false)

What breaks: setWorkflow(false) disables the business rules that would normally fire for that transaction — and that includes the platform logic responsible for writing to sys_audit. This is the clearly-established cause of a silently unaudited change.

autoSysFields(false) is usually reached for alongside it, and definitely stops sys_updated_on, sys_updated_by, and sys_mod_count from updating. Its precise relationship to audit recording itself is murkier — even experienced practitioners on ServiceNow's own community forums note they're not entirely certain whether it independently affects what gets written to sys_audit, separate from setWorkflow(false)'s effect. What's clear either way: used together, as they usually are, the change becomes close to invisible through every normal means — no audit entry, no updated timestamp, no bumped modification count.

gr.setWorkflow(false);
gr.autoSysFields(false);
gr.update();  // No audit logged!

Fix: Only use these methods when you intentionally want to suppress system impact, such as during fix scripts or data loads. Avoid them in production logic that should be tracked, and if you must use them on a compliance-relevant table, pair the call with explicit manual logging so the change isn't invisible everywhere.

2. Updating Fields With No Real Change

What breaks: If you set a field to the same value it already had, ServiceNow does not log an audit entry — even though setValue() was called. This one isn't a suppression bug at all; it's the platform correctly recognizing that nothing actually changed.

current.setValue('state', current.state);  // No actual change → no audit

Fix: Only set a field when the value truly differs.

if (current.state != 'Closed') {
    current.setValue('state', 'Closed');
}

3. Subflows or Script Includes Rewriting Values Later

What breaks: A flow or script may initially update a field, which gets audited correctly — but later logic silently overwrites the field again, often via setWorkflow(false) or from a different execution path entirely, with no second audit entry to show it happened.

Fix:

  • Trace flows and subflows for any post-processing that touches audited fields.
  • Add temporary business rules to log who's modifying what, and when, while you're actively debugging.

4. Bulk Updates via Import Sets or Data Sources

What breaks: Import Sets and Transform Maps often run with Run Business Rules unchecked, which skips audit logging entirely for every record the load touches — not just one.

Fix:

  • Enable Run Business Rules on the Transform Map where audit history actually matters.
  • If you're doing custom ETL outside a standard Transform Map, build logging into the load job itself.
  • For compliance-relevant tables (IRM tables among them), avoid audit-suppressed bulk updates wherever it's genuinely avoidable.

5. Auditing Not Enabled on the Field

What breaks: The most basic — but still common — reason: the field was never marked for audit in the first place.

Fix:

  • Go to System Definition > Dictionary.
  • Search for the field and confirm Audit = true.
  • Re-save if needed. You can enable auditing retroactively for a field, but be clear about what that actually recovers: it starts tracking changes from that point forward. It doesn't reconstruct history for changes that happened before auditing was turned on — there's no data to recover if it was never written.

6. A Field Excluded via the no_audit Attribute — Even Though the Table Is Audited

What breaks: This one is sneakier than #5, because the symptoms look identical from the outside. It's not possible to audit individual fields without auditing the entire table — but it is possible to hide specific fields from an otherwise-audited table using the no_audit dictionary attribute. A developer checking "is this table audited?" and finding yes can still miss that one specific field was deliberately excluded.

Fix: If a specific field isn't showing history despite the table clearly being audited elsewhere, check that field's own dictionary entry for a no_audit attribute before assuming something else is broken.

7. Whitelist (Inclusion-List) Auditing Silently Limiting Scope

What breaks: By default, enabling auditing on a table audits every field except system fields — an exclusion-list approach. But a table can instead be configured with audit_type=whitelist, which flips the model entirely: only fields explicitly marked as audited get tracked, and everything else is excluded by design, not by accident. If you're troubleshooting a table configured this way without knowing it, "why isn't this field audited" can send you looking for a bug that isn't there.

Fix: Check the table's dictionary attributes for audit_type=whitelist before assuming a missing audit entry is a defect. If it's whitelist-configured, the fix is adding the specific field to the audited list, not debugging business rules.

8. Built-In Platform Exclusions You Didn't Know About

What breaks: A few exclusions are built into the platform by design, and they catch people who haven't run into them before:

  • Changes to fields with the sys_ prefix are excluded from auditing, with the exception of sys_class_name and sys_domain_id.
  • Updates made by the inactivity monitor are excluded, specifically to avoid a single stale record generating a flood of noise entries.

Fix: Nothing to fix here technically — but knowing these exclusions exist saves real debugging time when a field that "should" be audited turns out to be a system field, or when a record's staleness-triggered update never shows up in history.

9. Async-Triggered Updates Crediting "System" Instead of the Real User

What breaks: This is a different flavor of problem entirely — the audit entry exists, but who it credits is misleading. When a record is updated via an asynchronous mechanism (a Script Action responding to an event, for instance), the update typically runs as the system account by default. Audit History then shows User Name "System" and an empty User field, rather than the person whose action actually triggered the underlying event — even though a real user's action was what set the whole chain in motion.

Fix: If accurate attribution matters for a specific async-triggered flow, the triggering event's originating user (often available via the event or the originating record's sys_created_by/sys_updated_by) can be captured and applied manually before the update, with autoSysFields(false) and manually-set sys_created_by/sys_updated_by values, then re-enabled afterward. This is more setup than the default behavior, so reserve it for cases where attribution genuinely matters — for most async processing, "System" being credited is expected and fine.

Bonus: How to Audit-Proof Your Work

  • Use gs.info() logs during development to trace field changes as you build, not just when something's already gone wrong.
  • Add temporary After Update business rules to catch unexpected changes while investigating a specific issue.
  • Periodically review the sys_audit table directly to confirm field-level tracking is behaving the way you expect, rather than assuming the Dictionary configuration alone guarantees it.
  • Consider building a custom audit summary dashboard for sensitive tables — compliance/IRM tables like sn_compliance_policy_exception or sn_risk_risk are good candidates, given how much an auditor might eventually care about exactly this data.
  • If audit history for older records seems to be missing even though the table has always been audited, check whether a data retention or table cleanup policy is configured to purge old sys_audit records after a set period — "missing" isn't always a bug; sometimes it's a retention policy that already did exactly what it was configured to do.

Conclusion

Field auditing is like insurance — you don't think about it until you need it. Whether it's for IRM, security, or HR, making sure your changes are properly tracked is a must. Some of the ways it breaks are careless code; others are the platform working exactly as designed, just in a way that surprises anyone who hasn't hit it before. Knowing the difference is most of the battle — avoid these nine pitfalls, and your ServiceNow platform gets meaningfully more transparent, reliable, and audit-ready.

No comments:

Post a Comment