tauri-apps/tauri · error

Permission not found, expected one of

Error message

Permission {} not found, expected one of {}

What it means

tauri-build's ACL validation (validate_capabilities) fails when a capability file references a permission_id that does not exist in any known plugin's or core's permission manifest. The error lists every available permission so the developer can correct the identifier. This is a build-time gate: bad ACL references would otherwise silently produce a mis-permissioned app.

Solutions

  1. Copy the exact identifier from the 'expected one of' list printed by the error into your capability file
  2. Add the plugin as a dependency in Cargo.toml/package.json so its permission manifest is registered
  3. Check for renames after upgrading a plugin — update capability files to the new permission names
  4. Fix the prefix format in the permission id (plugin-name:permission-name, core permissions use core: prefixes)

Example fix

// before (src-tauri/capabilities/default.json)
"permissions": ["fs:readfile", "shell-open"]
// after
"permissions": ["fs:read-file", "shell:allow-open"]
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN: &[&str] = &["core:default", "shell:allow-open", "fs:read-file", "opener:default"];
fn permissions_valid(perms: &[String]) -> bool {
  perms.iter().all(|p| KNOWN.contains(&p.as_str()))
}

Try / catch

// tauri_build::build() returns Result — fail the build script with context:
if let Err(e) = tauri_build::try_build(attrs) {
  eprintln!("ACL validation failed: {e}");
  std::process::exit(1);
}

Prevention

When it happens

Trigger: A `permissions` entry in src-tauri/capabilities/*.json names a permission (e.g. "fs:read-file" vs actual "fs:read-file" casing/path variants, or a plugin not installed) that no manifest declares; validate_capabilities runs during tauri build / build().

Common situations: Typo'd permission identifiers; referencing a plugin permission without adding the plugin as a dependency; permission renamed in a plugin upgrade; wrong `identifier` prefix (missing `plugin-name:` or extra prefix); hand-editing capability JSON.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of tauri-apps/tauri@460ec35447 (2026-09-18). Data as JSON: /api/errors/a61bc07dda2b2ae9. Report an issue: GitHub.

Appendix: source

Thrown at crates/tauri-build/src/acl.rs:388

        let mut available_permissions = Vec::new();
        for (key, manifest) in acl_manifests {
          let prefix = if key == APP_ACL_KEY {
            "".to_string()
          } else {
            format!("{key}:")
          };
          if manifest.default_permission.is_some() {
            available_permissions.push(format!("{prefix}default"));
          }
          for p in manifest.permissions.keys() {
            available_permissions.push(format!("{prefix}{p}"));
          }
          for p in manifest.permission_sets.keys() {
            available_permissions.push(format!("{prefix}{p}"));
          }
        }

        anyhow::bail!(
          "Permission {} not found, expected one of {}",
          permission_id.get(),
          available_permissions.join(", ")
        );
      }
    }
  }

  Ok(())
}

pub fn build(out_dir: &Path, target: Target, attributes: &Attributes) -> super::Result<()> {
  let mut acl_manifests = read_plugins_manifests()?;

  let app_acl = app_manifest_permissions(
    out_dir,
    attributes.app_manifest,
    &attributes.inlined_plugins,

View on GitHub (pinned to 460ec35447)