Skip to content

Send on-demand sync audits to S3, SQS, or SNS

Overview

When a PolicySync connector is configured with on-demand resource sync, it can publish one ON_DEMAND audit event per requested resource to the policysync_audit stream. Each event carries that resource's load outcome (found, added, updated, deleted, or not-found), its outcome codes, the policies applied during the sync, and the request context.

These events flow through the same Audit Server path as every other connector audit. Once you turn the feature on for the connector, the ON_DEMAND events reach whichever destinations you have already configured for the policysync_audit stream — Apache Solr, Amazon S3 (through Audit Fluentd), Amazon SQS, and Amazon SNS (through an SNS→SQS subscription).

Because the stream is indexed into Apache Solr, the events are also visible in Privacera Portal alongside your other audit records.

The feature is opt-in and controlled by a single connector setting. When it is off, on-demand syncs still run and apply grants exactly as before — only the per-resource ON_DEMAND audit is not produced.

There are two parts to this configuration:

  1. Point the policysync_audit stream at S3, SQS, or SNS using the standard destination configuration (Choose your destinations).
  2. Enable the ON_DEMAND audit on the connector so the events are produced (Setup).

Prerequisites

On-demand resource sync is configured and working on the connector, so the connector is polling the Ops Server for RESOURCE_SYNC tasks.

Choose your destinations

Choose where the audits go before you turn the feature on, so the events have somewhere to land as soon as the connector starts producing them.

ON_DEMAND events travel on the policysync_audit stream, so they follow the destinations you set for that stream. There is no separate on-demand destination list — configure the outputs once, and the on-demand events are included automatically.

Destination How to configure
Amazon S3 Route the stream through Audit Fluentd to S3. See Send Audits to S3, then optionally Query PolicySync Audits in Amazon Athena.
Amazon SQS Enable the sqs destination and set the queue URL and region. See Send Audits to Amazon SQS.
Amazon SNS Deliver to SQS from an SNS topic by subscribing your SQS queue to the topic, then configure the sqs destination as above.

You also need AUDITSERVER_POLICYSYNC_AUDIT_HANDLER_V2_ENABLED: true. See Connector Audit Output Destinations.

Note

Make sure the policysync_audit destination list (AUDITSERVER_POLICYSYNC_AUDIT_DESTINATIONS_NAMES) includes the destinations you want. Privacera Manager does not add sqs for you — see Connector Audit Output Destinations.

Setup

Follow these steps on the host where Privacera Manager is installed.

  1. Sign in to that host.
  2. Open the Manager configuration directory:
    Bash
    cd ~/privacera/privacera-manager/config
    
  3. Copy the connector sample variables into custom-vars if you have not already. The -n flag avoids overwriting an existing file. Replace the file name with the one for your connector (for example, vars.connector.databricks.unity.catalog.yml).
    Bash
    cp -n sample-vars/vars.connector.databricks.unity.catalog.yml custom-vars/
    
  4. Edit the connector custom variables file:
    Bash
    vi custom-vars/vars.connector.databricks.unity.catalog.yml
    
  5. Enable the on-demand audit by adding (or updating) the following property. This renders the connector property policysync.on-demand.audit.enable=true.

    Property Purpose Privacera Manager default
    CONNECTOR_ON_DEMAND_AUDIT_ENABLE Emit one ON_DEMAND audit event per requested resource, on the policysync_audit stream false

    Leaving this false only suppresses the audit. Resource load, grant application, and the Ops Server task-status flow behave the same either way.

YAML
CONNECTOR_ON_DEMAND_AUDIT_ENABLE: "true"
  1. Confirm on-demand processing is enabled for the connector (CONNECTOR_ON_DEMAND_PROCESSING_ENABLE: "true"); the audit is only produced when on-demand syncs actually run.
  2. Apply the configuration with Privacera Manager, then restart the connector.
Bash
1
2
3
cd ~/privacera/privacera-manager
./privacera-manager.sh setup
./pm_with_helm.sh upgrade 

Event format

ON_DEMAND is one more eventType on the policysync_audit stream, so it uses the same wire contract as every other PolicySync connector event: one JSON object per event, UTF-8, camelCase property names, and UPPERCASE enum-like string values. Unknown root or extraInfo keys may appear in future releases — ignore or store them unless you explicitly handle them. See Policy Tracing and Explainability for the shared contract and the other event types.

The same payload shape is indexed into Apache Solr and forwarded to Amazon S3, Amazon SQS, and Amazon SNS.

Envelope fields

An ON_DEMAND event fixes several envelope fields to constant values, because it is always produced by the resource loader running from an on-demand task.

Field Type Description
eventType String Always ON_DEMAND. Filter on this field to select on-demand syncs
evtTime String When the event was produced. ISO 8601, for example 2026-07-28T07:14:35Z
timeTakenMs Integer How long the sync took, in milliseconds. Every event from the same sync carries the same value
status String COMPLETED or FAILED for this one resource
msg String Readable outcome for this resource, taken from the outcome code
connectorType String Connector family, for example databricks_unity_catalog
connector String Connector instance name
changeLogId Integer Identifies the sync run. Every event from the same sync carries the same value
changeLogType String Always SERVICE_RESOURCES
loaderType String Always LOAD_SERVICE_RESOURCES
triggerFrom String Always EVENT, meaning an Ops Server request triggered the sync rather than the schedule
ondemandTaskId Integer Identifies the on-demand request. Use it to group every resource event from one request
uuid String Document identifier assigned by the destination. May be null
extraInfo Object The on-demand payload. Holds the outcome codes, what happened, and what was requested

A few of these fields need more than a one-line description:

  • eventType — a scheduled resource load reports LOADER instead, so filtering on ON_DEMAND selects only the syncs that were explicitly requested.
  • status — the outcome for this one resource, so one request can produce both COMPLETED and FAILED events. The exception is a failed ACL operation, which counts for the whole task: every resource in the request that reached grant application is then FAILED, even if its own grants applied. See Outcome codes.
  • msg — fixed text for the outcome, safe to show to a user. It never contains the target service's error text, which is in errorDetails.serviceError. The wording can change between releases, so code should read extraInfo.response[] instead.
  • timeTakenMs — measured from the start of the resource load to the moment the event is produced.

Output: what the sync did

The extraInfo.output object describes what happened to this resource. The outcome codes are next to it, in extraInfo.response; see Outcome codes.

Field Type Description
resources Object The change counts for this resource, keyed by its type — table, schema, catalog, and so on
policies Array The policies applied during this sync, each with its ACL counts
policiesTruncated String Optional. Reads kept/total, for example 40/112, when the policy list was shortened to fit the audit size limit
aclsExecuted Object Optional. ACL totals of the policies applied in this sync
errorDetails Object Optional. Why the outcome failed

When a field is empty rather than absent:

  • resources is {} when the resource was not found, when the load failed, or when the request could not be matched to a single resource.
  • policies is [] when this resource did not change, because no grants were applied to it.
  • aclsExecuted is absent when the resource did not change. It is present on successful events too, not only on failures.
  • errorDetails is absent on a success, because there is nothing to report.

Resource counts

The value under output.resources.<type>. Each count is 0 or 1, because one event describes exactly one requested resource.

Field Type Description
found Integer 1 when the resource exists in the source. A deleted resource is no longer there, so it has found 0 and deleted 1
added Integer 1 when the resource was newly added to the connector store
updated Integer 1 when an existing resource was updated
deleted Integer 1 when the resource was removed from the source and its grants were revoked

The policies that were applied

Each entry of output.policies:

Field Type Description
id String Policy identifier
version String Policy version
name String Policy name
label String Correlation or trace label; empty when the policy carries none
aclsExecuted Object ACL counts for this policy: total, applied, failed, skipped, and timeTakenMS
failedReasons Object The target service's error text, mapped to how many ACL operations failed with it. {} when nothing failed. Not normalized: the text is as the connector wrote it and can include table and principal names, so treat it as sensitive. AWS Lake Formation normalizes its own but keeps single-word names
skippedReasons Object Each skip reason, mapped to how many ACL operations were skipped for it. {} when nothing was skipped
ownerIgnore Object Optional. How many ACL operations were skipped because the principal owns the resource

ACL totals

The output.aclsExecuted object adds up the ACL operations of the policies applied in this on-demand sync. Read the counts here, whatever the outcome.

Note

The totals, like response[], cover the whole on-demand sync, not only this resource. When one sync covers several resources, each changed resource's event reports the same totals.

Field Type Description
total Integer ACL operations attempted in this sync
applied Integer ACL operations that succeeded
failed Integer ACL operations that failed. Any value above 0 makes this event FAILED, even when the sync completed overall
skipped Integer ACL operations that were skipped rather than attempted

Absent when the resource did not change, because no grants ran for it.

Truncated policy lists

These totals are computed across all policies in the sync, before any truncation. When policiesTruncated is present, aclsExecuted can therefore describe more policies than policies lists. That is deliberate: the counts stay accurate even when the list is shortened, so a failure inside a truncated-away policy is still visible.

Why the outcome failed

The output.errorDetails object explains a failure. It never appears on a successful outcome, and it does not appear on every failed one either — a resource that never reached grant application has no cause to report. When it is present, it carries only the parts that apply.

Field Type Description
topReasons Array Optional. The most common failure reasons in this sync, as {reason, count} objects. Present only when grants were attempted. Not normalized, like policies[].failedReasons: the text can include table and principal names
serviceError String Optional. The error from the target service, or from the sync, normalized as described below and at most 512 characters long

Which of the two appear depends on the outcome; see Outcome codes. topReasons is a summary, and policies[].failedReasons has the per-policy breakdown. ACL counts are in ACL totals, and the failure's codes in extraInfo.response[].

In serviceError, ARNs, email addresses, IPv4 addresses, request IDs, and quoted or dotted object names are replaced with ?. A name with none of these shapes, such as a single-word table name, is kept, so treat the text as diagnostic data.

Input: the original request

The extraInfo.input object echoes back the request this event answers, so you can match an event to its request without calling the Ops Server.

Field Type Description
createTime Integer When the request was created, in epoch milliseconds
syncType String The request type. RESOURCE_SYNC for on-demand resource syncs
source String Which Ops Server source the request arrived through, for example REST_API or MSK
appType String The type of application that made the request, for example PS_CONNECTOR
resources Array Holds one entry: the resource this event describes

The source value is the OPS_BRIDGE_SOURCE name configured for the connector, so it names the path the request came in through — the REST API, MSK, or another configured source. See On-Demand Sync for PolicySync Connectors.

Each element of input.resources[]:

Field Type Description
type String The requested resource type, such as table, schema, or catalog
values Object The resource as it was requested, such as catalog, schema, and table, plus the requestor fields below

Inside input.resources[].values:

Field Type Description
requestor_id String Who requested this resource. Present when the request supplied it
requestor_create_time String When this resource was requested, in epoch seconds

These two fields are per resource, not per request. If one request asks for several resources on behalf of different people, each event carries its own requestor.

Sample event

One resource (table1) added by on-demand task 6, with one policy applied:

JSON
{
  "eventType": "ON_DEMAND",
  "evtTime": "2026-07-28T07:14:35Z",
  "timeTakenMs": 2140,
  "status": "COMPLETED",
  "msg": "Resource synced; access grants applied",
  "connectorType": "databricks_unity_catalog",
  "connector": "databricks-unity-catalog-instance1",
  "changeLogId": 18472,
  "changeLogType": "SERVICE_RESOURCES",
  "loaderType": "LOAD_SERVICE_RESOURCES",
  "triggerFrom": "EVENT",
  "ondemandTaskId": 6,
  "extraInfo": {
    "response": [
      {
        "code": 2001,
        "msg": "Resource synced; access grants applied",
        "origin": "DBX",
        "count": 3
      }
    ],
    "output": {
      "resources": {
        "table": {
          "found": 1,
          "added": 1,
          "updated": 0,
          "deleted": 0
        }
      },
      "policies": [
        {
          "id": "160",
          "version": "12",
          "label": "",
          "name": "policy1",
          "aclsExecuted": {
            "total": 3,
            "applied": 3,
            "failed": 0,
            "skipped": 0,
            "timeTakenMS": 717
          },
          "failedReasons": {},
          "skippedReasons": {},
          "ownerIgnore": {
            "count": 0
          }
        }
      ],
      "aclsExecuted": {
        "total": 3,
        "applied": 3,
        "failed": 0,
        "skipped": 0
      }
    },
    "input": {
      "createTime": 1785222862000,
      "syncType": "RESOURCE_SYNC",
      "source": "REST_API",
      "appType": "PS_CONNECTOR",
      "resources": [
        {
          "type": "table",
          "values": {
            "catalog": "catalog1",
            "schema": "schema1",
            "table": "table1",
            "requestor_id": "6bab473e-612f-450b-a99c-e6a1d5de2424",
            "requestor_create_time": "1785222861"
          }
        }
      ]
    }
  }
}

All three ACL operations applied, so response has one entry, 2001 SYNCED_GRANTS_APPLIED, with count 3.

A request for three resources produces three events like this one, all sharing ondemandTaskId, changeLogId, and timeTakenMs.

Outcome codes

Every requested resource gets one event. Its status and msg give the resource's outcome, and extraInfo.response[] lists its outcome codes, on success and on failure, in the same shape as on ACCESS and POLICY_RESPONSE events. Branch on response[].code, not on msg: the codes stay the same between releases, the wording may not. For the fields of an entry, see The response entry.

Where each one shows up

Privacera Portal shows msg in the Message column of the audit list, for operators. response[] is in the event payload, not a Portal column, for consumers reading the events from Solr, S3, Amazon SQS, or Amazon SNS. Both describe the same outcome.

Outcome status msg When it happens errorDetails
SYNCED_GRANTS_APPLIED COMPLETED Resource synced; access grants applied The resource changed and a matching policy applied its grants —
NO_POLICY_FOUND COMPLETED Resource synced; no matching policy found, so no grants were applied The resource changed, but no policy matched it, so there was nothing to grant —
NO_CHANGE_DETECTED COMPLETED No change detected since last sync, grant not reapplied The resource was loaded and is unchanged since the last sync, so grants were not re-applied —
RESOURCE_DELETED COMPLETED Resource deleted from source The resource no longer exists in the source and was removed; its grants were revoked —
RESOURCE_NOT_FOUND FAILED Resource not found in source; nothing to apply The requested resource does not exist in the source, so nothing was synced and no grants were applied none — it never entered grant application, so the sync's grant error is not its cause
RESOURCE_LOAD_FAILED FAILED Resource load failed The resource load itself did not complete (for example the source could not be reached, or the resource key could not be persisted) serviceError
GRANT_FAILED FAILED Failed to grant Grant application ran for this resource and all ACL operations failed, or grant application failed without a failed ACL operation topReasons, serviceError
GRANT_PARTIALLY_APPLIED FAILED Grants partially applied; some grants failed Grant application ran and some ACL operations succeeded while others failed topReasons, serviceError

What response[] lists

  • A resource that reached grant application (it changed, and the on-demand task ran ACL operations) lists the outcomes of the task's ACL operations, grouped, with a count each. For example, 2001 for an applied grant, 1004 for a grant skipped because the grantee owns the resource, or 5001 for a transient failure. The codes are in ON_DEMAND codes and template-based row filter codes; see also the limitation on which ACL operations are counted.
    • These entries cover the whole task, not this resource alone. Every changed resource in the request lists the same ones, so do not add up count across a request's events.
    • A resource whose outcome is RESOURCE_DELETED also lists 2004, count 1, for example [{2001, count 3}, {2004, count 1}]. If the load, grant application, or any ACL operation of the task failed, its outcome is that failure and it lists no 2004. To detect a deletion in every case, read output.resources.<type>.deleted.
  • Any other resource lists its own outcome, one entry with count 1, from the table below.
  • When the resource load fails, every resource in the request also lists the load failure's code: 4505 RESOURCE_LOAD_FAILED; 5001 SYSTEM_ERROR when it is transient; 1006 RESOURCE_NOT_FOUND or 4503 PRINCIPAL_NOT_FOUND when the backend reports a missing resource or principal.
  • A FAILED event never lists only SUCCESS entries.

The resource-level codes:

code strCode Category Meaning
1006 RESOURCE_NOT_FOUND EXPECTED The resource does not exist in the source, for example a dropped table, so nothing was applied. The event status is FAILED
2001 SYNCED_GRANTS_APPLIED SUCCESS The resource changed and a matching policy applied its grants
2002 NO_CHANGE_DETECTED SUCCESS The resource is unchanged since the last sync, so grants were not re-applied
2003 NO_POLICY_FOUND SUCCESS The resource changed, but no policy matched it, so no grants were applied
2004 RESOURCE_DELETED SUCCESS The resource no longer exists in the source; it was removed and its grants were revoked
4502 GRANT_FAILED PERMANENT The resource changed, but grant application failed with no failed ACL operation, for example before any ACL operation ran or because the changelog failed after the grants applied. When grant application could not start and the failure was classified, the entry is that classification instead, such as 5001
4505 RESOURCE_LOAD_FAILED PERMANENT The resource load did not complete, for a reason that is neither transient nor a not-found error, or that could not be classified. Reported for every resource in the request
5001 SYSTEM_ERROR TRANSIENT The resource load, or the start of grant application, failed for an infrastructure reason, such as throttling or a dropped connection. A load interrupted by a connector restart is also reported this way, with origin PRIVACERA, on every resource in the request. A new request may succeed

Which resources a failure affects

Outcome Scope within the request
RESOURCE_NOT_FOUND Only the missing resource. A load failure the backend reports as not found is RESOURCE_LOAD_FAILED instead, and lists 1006 on every resource in the request
RESOURCE_LOAD_FAILED Every resource in the request, because the load did not finish
GRANT_FAILED, GRANT_PARTIALLY_APPLIED Only resources that changed, since only those reached grant application; all of them when any ACL operation of the task failed

Partial grants are detected from the counts

GRANT_PARTIALLY_APPLIED is decided from output.aclsExecuted, not from the changelog status. Individual ACL operations can fail while the sync completes overall, so a resource where 4 of 6 grants applied is reported as a partial failure rather than a clean success.

Not found is not the same as deleted

  • A deleted resource existed, was removed from the source, and had its grants revoked. That is successful work, so RESOURCE_DELETED is COMPLETED.
  • A not-found resource never existed as requested, so nothing was synced. RESOURCE_NOT_FOUND is FAILED so that the request is flagged. Its entry is 1006, category EXPECTED: there is nothing to retry.

Requests that cannot be matched to one resource

A coarse or partial request — for example a parent path whose children are keyed individually — cannot always be matched back to a single resource's load result. Such an entry reports NO_CHANGE_DETECTED with empty resources and policies. The sync itself still ran; only the per-resource attribution is unavailable. Two consequences worth knowing when reading these events: a coarse re-sync whose child resources did change still reports "no change" for the parent entry, and such an entry stays COMPLETED even when grant application failed elsewhere in the same sync.

Failed use-cases

A failed outcome always emits status = FAILED, a readable msg from the table above, its codes in extraInfo.response[], and — where there is a cause to report — the underlying error in errorDetails.serviceError.

msg never carries the raw error

msg comes only from the outcome code, so it is safe to display. The error text is in other fields; read them for diagnosis:

  • errorDetails.serviceError: the target service's error, with ARNs, request IDs, and similar identifiers replaced. See Why the outcome failed.
  • errorDetails.topReasons and policies[].failedReasons: each ACL operation's failure text as the connector wrote it, which can include table and principal names. AWS Lake Formation normalizes its own like serviceError, but keeps single-word names.
  • output.lfForward.reason: the raw cause.

Treat these fields as sensitive, for every connector, when you route ON_DEMAND events to Amazon SQS, Amazon SNS, or Amazon S3. The same text also reaches POLICY_RESPONSE and ACCESS events; see POLICY_RESPONSE.

Resource not found

output.resources and output.policies are empty, and there is no errorDetails: the resource never reached grant application. input still shows which resource was requested. response[] has one entry, 1006 RESOURCE_NOT_FOUND, category EXPECTED.

JSON
{
  "eventType": "ON_DEMAND",
  "evtTime": "2026-07-28T07:15:34Z",
  "timeTakenMs": 403,
  "status": "FAILED",
  "msg": "Resource not found in source; nothing to apply",
  "connectorType": "databricks_unity_catalog",
  "connector": "databricks-unity-catalog-instance1",
  "changeLogId": 18479,
  "changeLogType": "SERVICE_RESOURCES",
  "loaderType": "LOAD_SERVICE_RESOURCES",
  "triggerFrom": "EVENT",
  "ondemandTaskId": 8,
  "extraInfo": {
    "response": [
      {
        "code": 1006,
        "msg": "Resource not found in source; nothing to apply",
        "origin": "DBX",
        "count": 1
      }
    ],
    "output": {
      "resources": {},
      "policies": []
    },
    "input": {
      "createTime": 1785222921000,
      "syncType": "RESOURCE_SYNC",
      "source": "REST_API",
      "appType": "PS_CONNECTOR",
      "resources": [
        {
          "type": "table",
          "values": {
            "catalog": "catalog1",
            "schema": "schema1",
            "table": "does_not_exist",
            "requestor_id": "7bfaed32-8c8d-40b8-b122-0580cae8cfcc",
            "requestor_create_time": "1785222921"
          }
        }
      ]
    }
  }
}

Resource load failed

The load did not complete, so every resource in the task is FAILED with the same code, and output.resources is empty on all of them. The load error itself is in errorDetails.serviceError, not in msg. This failure is not transient, so the response[] entry is 4505 RESOURCE_LOAD_FAILED; a transient load failure is 5001 SYSTEM_ERROR, which is worth retrying.

JSON
{
  "eventType": "ON_DEMAND",
  "evtTime": "2026-07-28T07:16:10Z",
  "timeTakenMs": 512,
  "status": "FAILED",
  "msg": "Resource load failed",
  "connectorType": "databricks_unity_catalog",
  "connector": "databricks-unity-catalog-instance1",
  "changeLogId": 18485,
  "changeLogType": "SERVICE_RESOURCES",
  "loaderType": "LOAD_SERVICE_RESOURCES",
  "triggerFrom": "EVENT",
  "ondemandTaskId": 9,
  "extraInfo": {
    "response": [
      {
        "code": 4505,
        "msg": "Resource load failed",
        "origin": "DBX",
        "count": 1
      }
    ],
    "output": {
      "resources": {},
      "policies": [],
      "errorDetails": {
        "serviceError": "resource key persist failed"
      }
    },
    "input": {
      "createTime": 1785222970000,
      "syncType": "RESOURCE_SYNC",
      "source": "REST_API",
      "appType": "PS_CONNECTOR",
      "resources": [
        {
          "type": "table",
          "values": {
            "catalog": "catalog1",
            "schema": "schema1",
            "table": "table1",
            "requestor_id": "5f2a1c9e-3b7d-4e21-9a44-1c0b2d8e6f30",
            "requestor_create_time": "1785222969"
          }
        }
      ]
    }
  }
}

Grant application partially applied

The resource changed, so the event has its counts and policy list, as a COMPLETED event would.

  • output.aclsExecuted shows how far the grants got: 1 of 3 ACL operations applied and 2 failed. That is a partial grant, so the outcome is GRANT_PARTIALLY_APPLIED, not GRANT_FAILED.
  • The target's error text is in errorDetails.topReasons, and per policy in policies[].failedReasons.
  • response[] lists both outcomes: one grant applied (2001), and two failed because the grantee does not exist (4503 PRINCIPAL_NOT_FOUND), which a retry does not fix.
JSON
{
  "eventType": "ON_DEMAND",
  "evtTime": "2026-07-28T07:16:44Z",
  "timeTakenMs": 1633,
  "status": "FAILED",
  "msg": "Grants partially applied; some grants failed",
  "connectorType": "databricks_unity_catalog",
  "connector": "databricks-unity-catalog-instance1",
  "changeLogId": 18490,
  "changeLogType": "SERVICE_RESOURCES",
  "loaderType": "LOAD_SERVICE_RESOURCES",
  "triggerFrom": "EVENT",
  "ondemandTaskId": 10,
  "extraInfo": {
    "response": [
      {
        "code": 4503,
        "msg": "Grantee principal not found in the target service",
        "origin": "DBX",
        "serviceCode": "PRINCIPAL_DOES_NOT_EXIST.PRINCIPAL_DOES_NOT_EXIST",
        "count": 2
      },
      {
        "code": 2001,
        "msg": "Resource synced; access grants applied",
        "origin": "DBX",
        "count": 1
      }
    ],
    "output": {
      "resources": {
        "table": {
          "found": 1,
          "added": 1,
          "updated": 0,
          "deleted": 0
        }
      },
      "policies": [
        {
          "id": "160",
          "version": "12",
          "label": "",
          "name": "policy1",
          "aclsExecuted": {
            "total": 3,
            "applied": 1,
            "failed": 2,
            "skipped": 0,
            "timeTakenMS": 604
          },
          "failedReasons": {
            "[PRINCIPAL_NOT_FOUND | retryable=false] PRINCIPAL_DOES_NOT_EXIST.PRINCIPAL_DOES_NOT_EXIST (State: 42704)": 2
          },
          "skippedReasons": {},
          "ownerIgnore": {
            "count": 0
          }
        }
      ],
      "aclsExecuted": {
        "total": 3,
        "applied": 1,
        "failed": 2,
        "skipped": 0
      },
      "errorDetails": {
        "topReasons": [
          {
            "reason": "[PRINCIPAL_NOT_FOUND | retryable=false] PRINCIPAL_DOES_NOT_EXIST.PRINCIPAL_DOES_NOT_EXIST (State: 42704)",
            "count": 2
          }
        ],
        "serviceError": "changelog status=FAILED"
      }
    },
    "input": {
      "createTime": 1785223004000,
      "syncType": "RESOURCE_SYNC",
      "source": "REST_API",
      "appType": "PS_CONNECTOR",
      "resources": [
        {
          "type": "table",
          "values": {
            "catalog": "catalog1",
            "schema": "schema1",
            "table": "table1",
            "requestor_id": "a71e0d64-9c52-4f18-8b30-2e6f5a1d7c93",
            "requestor_create_time": "1785223003"
          }
        }
      ]
    }
  }
}

When every grant fails

An event where none of the ACL operations succeeded has the same shape but reports GRANT_FAILED with msg Failed to grant, and output.aclsExecuted shows applied: 0. The distinction matters when reading these events: GRANT_PARTIALLY_APPLIED means some access was granted, GRANT_FAILED means none was.

Validate

In Privacera Portal

The audits are indexed into Apache Solr, which is what Privacera Portal reads, so you can confirm the feature is working from the Portal without querying anything directly.

  1. Sign in to Privacera Portal.
  2. From the application menu at the top, select Access Controls.
  3. In the left navigation, select Audits.
  4. Select the Policy Sync tab.
  5. Adjust the date range at the top right if your sync is older than the default window, and use the search box to narrow the list.
  6. The tab lists every PolicySync event, so narrow it to the rows where Event Type is ON_DEMAND.

Each row is one requested resource. Read the outcome from these columns:

Column What it shows
Event Type ON_DEMAND for a requested sync
Status COMPLETED or FAILED for that one resource
Message The readable outcome, for example Resource not found in source; nothing to apply
Ondemand Task ID Groups every resource from the same request
Time Taken How long the sync took

To review one request end to end, note its Ondemand Task ID and look at every row sharing that value.

If a sync produced no ON_DEMAND rows at all, the audit is most likely not enabled on the connector — check CONNECTOR_ON_DEMAND_AUDIT_ENABLE.

In Prometheus

The connector publishes one counter for on-demand audit emission, so you can confirm events are being produced — and spot events that failed to reach the audit pipeline at all, which by definition never reach the Portal or Solr.

Metric Description
ps_core_ondemand_audit_events_total One increment per ON_DEMAND audit event. Tagged connector, result, status, stage, and path
Tag Values Meaning
connector connector instance name Which connector produced the event
result emitted, failed Whether the event reached the audit pipeline
status COMPLETED, FAILED, none The audit outcome the event reports
stage build, send, none Where emission broke, if it did
path normal, processor_construction, stats_construction How the emission was reached

Useful queries:

Text Only
1
2
3
4
sum by (result) (ps_core_ondemand_audit_events_total)
sum by (status) (ps_core_ondemand_audit_events_total{result="emitted"})
ps_core_ondemand_audit_events_total{result="failed"}
ps_core_ondemand_audit_events_total{path!="normal"}

Any non-zero result="failed" means an audit event was produced but never delivered — worth alerting on. A non-zero path!="normal" means something upstream failed while the audit was still emitted.

path is not an outcome

A fallback emission is still counted under result="emitted". Do not treat path as an alternative to success or failure, or the same events will be counted twice.

In Amazon SQS

Use the built-in test script inside the Audit Server diagnostics container to confirm ON_DEMAND records are arriving in your queue.

Warning

Setting DELETE_AFTER_PROCESSING to true permanently removes messages from the queue after the script reads them. Use false to inspect messages without consuming them.

  1. Open a shell inside the Audit Server diagnostics container.
  2. Navigate to the SQS test directory:
    Bash
    cd /workdir/manual_test/read_sqs
    
  3. Set the required environment variables. Replace <account-id> and <queue-name> with your AWS account ID and queue name.
    Bash
    export SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/<account-id>/<queue-name>
    export DELETE_AFTER_PROCESSING="false"
    
  4. Run the script:
    Bash
    ./run_read_sqs.sh
    

The script prints the messages it reads. Look for records with "eventType":"ON_DEMAND" to confirm on-demand audits are flowing to the queue.

Guidelines

  • The ON_DEMAND audit is per resource — an on-demand request for N resources produces N audit records. Keep this in mind when sizing a large batch, and watch the SQS/SNS 256 KB per-message limit for very large, policy-heavy syncs.
  • Build consumers against extraInfo.response[], not msg. New codes may be added across releases. See Response codes.
  • Enabling CONNECTOR_ON_DEMAND_AUDIT_ENABLE does not change the Ops Server / task-status flow; that continues to work whether the flag is on or off.
  • If you filter an Amazon SNS subscription by event type, allow eventType = ON_DEMAND — otherwise the on-demand events are dropped before they reach the subscribed queue even though the connector emitted them.

Applies to

Self-Managed Privacera only. PrivaceraCloud customers should contact their Privacera representative. This configuration applies to the per-resource ON_DEMAND audit on the policysync_audit collection.