The ticket says "I can't attach my notebook to the cluster", "the Run now button is greyed out" or "PERMISSION_DENIED: User does not have CAN_USE on warehouse". These errors come from workspace object permissions (ACLs on clusters, jobs, notebooks, folders, warehouses, pipelines and pools). They are not Unity Catalog grants. Changing GRANT SELECT won't fix them. This guide shows how to find which permission is missing and how to add it without removing anyone else's access.
Note
Two permission systems apply. Workspace object ACLs decide who can use or change compute, jobs and code. Unity Catalog grants decide who can read or write data. If the error mentions a table, schema or catalog, see the Unity Catalog grants cheat sheet instead.
Symptom → missing permission
| What the user sees | Object | Permission they need |
|---|---|---|
| Cluster isn't in the attach list, or "does not have Attach permission" | Cluster | CAN_ATTACH_TO |
| Can attach but can't start a terminated cluster | Cluster | CAN_RESTART |
| Can't edit the cluster config, libraries or permissions | Cluster | CAN_MANAGE |
| Can see the job but Run now / Cancel is disabled | Job | CAN_MANAGE_RUN |
| Can't edit tasks, schedule or notifications | Job | CAN_MANAGE (or owner) |
| Job not visible in the Jobs list | Job | CAN_VIEW |
| Notebook opens read-only, can't run cells | Notebook / folder | CAN_RUN (run) or CAN_EDIT (change) |
| "PERMISSION_DENIED ... CAN_USE" on a SQL warehouse | Warehouse | CAN_USE |
| Can't see other users' queries on the warehouse | Warehouse | CAN_MONITOR |
| Can't create a cluster with a given policy | Cluster policy | CAN_USE on the policy |
| Pool missing from the cluster form | Instance pool | CAN_ATTACH_TO |
Level names and the set available per object type can change between releases. Check them on your workspace with databricks permissions get-permission-levels <object-type> <id>.
Step 1: Confirm who the user is and which groups they're in
Many "missing permission" tickets turn out to be group problems. The user isn't in the group that has the ACL, or SCIM sync hasn't updated yet.
# As the user (or with their profile): who does Databricks think I am?
databricks current-user me -p prod -o json | jq '{userName, groups: [.groups[].display]}'
# As an admin: look up a user and their direct groups
databricks users list --filter 'userName eq "priya@contoso.com"' -p prod -o json \
| jq '.[] | {id, userName, active, groups: [.groups[]?.display]}'
If a group is missing here but exists in Entra ID, the problem is with sync or automatic identity management, not with the ACL. Check the account console provisioning logs before you change permissions.
Tip
Groups synced from Entra ID can take a while to reach the workspace. If the user was added to the Entra group a few minutes ago, have them sign out and back in before you change anything.
Step 2: Read the ACL on the object
The permissions command group works across object types:
# Cluster
databricks permissions get clusters 1015-084512-abcd1234 -p prod -o json \
| jq -r '.access_control_list[] | [(.user_name // .group_name // .service_principal_name), (.all_permissions[] | "\(.permission_level)\(if .inherited then " (inherited)" else "" end)")] | @tsv'
# Job
databricks permissions get jobs 123456 -p prod -o json
# SQL warehouse
databricks permissions get warehouses 1a2b3c4d5e6f7a8b -p prod -o json
# Cluster policy
databricks permissions get cluster-policies 0012AB34CD56EF78 -p prod -o json
Notebooks and folders take a numeric object ID, not a path. Get it first:
databricks workspace get-status /Workspace/Shared/etl/load_sales -p prod -o json | jq '.object_id, .object_type'
databricks permissions get notebooks 4211983475561234 -p prod -o json
databricks permissions get directories 4211983475560001 -p prod -o json
Entries marked inherited come from a parent folder or from the workspace admins group. If the user's permission is inherited from a folder, change the folder rather than adding direct ACLs to every notebook in it.
Effective access in the UI
In the UI, open the object, then Permissions (or Share on notebooks). It shows the same ACL. For jobs, also check Run as. A job that runs as a service principal still needs that principal to have access to the cluster, cluster policy and data.
Step 3: Add the permission without wiping the ACL
There are two write verbs, and they behave very differently:
| Command | HTTP | Effect |
|---|---|---|
databricks permissions update | PATCH | Adds or changes the entries you send. Leaves the others as they are |
databricks permissions set | PUT | Replaces all direct entries with what you send |
Caution
permissions setwith a single entry removes every other direct grant on the object, including the ones for the job's support team or the service principal that runs it. During an incident, useupdate. Only usesetwhen you deliberately want to reset the ACL and you've saved the current one to a file first.
Grant a group attach and restart on a cluster:
databricks permissions update clusters 1015-084512-abcd1234 -p prod --json '{
"access_control_list": [
{"group_name": "data-engineers", "permission_level": "CAN_RESTART"}
]
}'
Levels are cumulative. CAN_RESTART includes attach, and CAN_MANAGE includes both.
Let on-call run and cancel a job without being able to edit it:
databricks permissions update jobs 123456 -p prod --json '{
"access_control_list": [
{"group_name": "oncall-data", "permission_level": "CAN_MANAGE_RUN"}
]
}'
Give a service principal use of a warehouse (use its application ID):
databricks permissions update warehouses 1a2b3c4d5e6f7a8b -p prod --json '{
"access_control_list": [
{"service_principal_name": "9f8e7d6c-1111-2222-3333-444455556666", "permission_level": "CAN_USE"}
]
}'
In PowerShell, inline JSON quoting breaks easily. Write the body to a file and pass --json @acl.json.
Back up before you reset
databricks permissions get jobs 123456 -p prod -o json > acl-job-123456-$(date +%F).json
To restore it, take the non-inherited entries from the access_control_list, rewrite each one as {principal, permission_level}, and send them with permissions set.
Step 4: Find out who changed it
If access worked yesterday, someone or something changed the ACL. Usual suspects are a bundle deploy, a Terraform apply or a manual set. Audit logs record permission changes:
SELECT event_time,
user_identity.email AS changed_by,
service_name,
action_name,
request_params
FROM system.access.audit
WHERE event_date >= current_date() - INTERVAL 7 DAYS
AND (action_name ILIKE '%acl%' OR action_name ILIKE '%permission%')
ORDER BY event_time DESC
LIMIT 100;
Action names vary by service and release (for example cluster and job ACL changes are logged under different services). Run the query without the action_name filter for one known change to see the exact names your workspace records.
If the change came from a service principal that runs your CI/CD, check the bundle or Terraform code. Both tools treat the permissions they manage as the full list. Anything granted by hand in the UI is removed on the next deploy.
Common traps
| Trap | What happens | Fix |
|---|---|---|
| ACL granted to a user, not a group | Breaks when the person changes team or leaves | Grant to groups; keep users out of ACLs |
| Permission on the notebook, job runs from a Git folder | Job reads a different path | Grant on the Git folder path the job actually uses |
User has CAN_ATTACH_TO but the cluster uses dedicated (single user) access mode | Only the assigned user or group can run commands on it | Change the assigned principal or use standard access mode |
CAN_MANAGE_RUN on job but the job's cluster policy is restricted | Repair or run-now with a new cluster spec fails | Grant CAN_USE on the policy to the run-as identity |
| Workspace admin can do it, user can't | Admins bypass object ACLs | Test as a normal user or with --debug using their profile |
Prevent it next time
- Manage ACLs in code (Asset Bundle
permissions:blocks or the Terraformdatabricks_permissionsresource) and stop granting by hand in production. Mixing the two causes drift that gets removed on the next deploy. - Grant to Entra-synced groups only, with a naming pattern such as
dbx-prod-<team>-<role>. - Give on-call
CAN_MANAGE_RUNon production jobs andCAN_RESTARTon shared clusters, so they can recover without needing admin rights. - Set folder-level permissions on
/Workspace/Shared/<team>and let notebooks inherit them. - Schedule the audit query above as a daily alert on permission changes in the production workspace.