tauri-errors-permissions · v1.0 · 2026-03-19 · sha256 5d094daff70310e5
tauri-errors-permissions v1.0A
Immutable. This exact content is served forever at /api/v1/blob/5d094daff70310e5.
---
name: tauri-errors-permissions
description: "Guides debugging and resolving Tauri 2 permission errors including capability misconfiguration, missing plugin permissions, scope violations, CSP violations, core vs plugin permission confusion, and step-by-step debugging workflows. Activates when encountering permission denied errors, CSP violations, or capability configuration issues."
license: MIT
compatibility: "Designed for Claude Code. Requires Tauri 2.x."
metadata:
author: Impertio
version: "1.0"
---
# tauri-errors-permissions
## Diagnostic Decision Tree
```
Permission-related error in Tauri 2
|
+-- Error: "command <name> not allowed"
| |
| +-- Plugin command (e.g., fs, http, dialog)?
| | --> Section 1: Missing Plugin Permissions
| |
| +-- Custom #[tauri::command]?
| | --> Section 2: Custom Command Permissions
| |
| +-- Core feature (window, event, path, menu, tray)?
| --> Section 3: Core Permissions
|
+-- Error: CSP violation (console shows "Refused to ...")
| --> Section 4: CSP Violations
|
+-- Error: scope violation / path access denied
| --> Section 5: Scope Violations
|
+-- No error but feature silently does nothing
| --> Section 6: Silent Permission Failures
|
+-- Build error related to permissions / capabilities
| --> Section 7: Configuration Format Errors
```
---
## Section 1: Missing Plugin Permissions
### Symptoms
- Runtime error: `command <plugin>|<command> not allowed`
- Plugin API call throws immediately after `invoke()`
- Works in dev but fails in production (or vice versa)
### Debugging Workflow
**Step 1.1**: Verify the plugin is registered in Rust (`src-tauri/src/lib.rs`):
```rust
tauri::Builder::default()
.plugin(tauri_plugin_fs::init()) // REQUIRED for each plugin
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_http::init())
```
**Step 1.2**: Verify permissions exist in a capability file. Check `src-tauri/capabilities/default.json`:
```json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"fs:default",
"dialog:default",
"http:default"
]
}
```
**Step 1.3**: Verify the specific command is covered. `<plugin>:default` grants a safe subset. Some commands require explicit permission:
| Plugin | Commands NOT in `:default` | Required Permission |
|--------|---------------------------|-------------------|
| `fs` | write operations | `fs:allow-write-file`, `fs:allow-mkdir`, `fs:allow-remove` |
| `shell` | execute, spawn | `shell:allow-execute`, `shell:allow-spawn` |
| `clipboard-manager` | all operations | `clipboard-manager:allow-read-text`, `clipboard-manager:allow-write-text` |
| `http` | requests to URLs | Requires URL scope configuration |
| `notification` | request-permission | `notification:allow-request-permission` |
**Step 1.4**: Verify the `windows` array includes the window making the call:
```json
{
"windows": ["main"],
"permissions": ["fs:default"]
}
```
If the calling window label is `"settings"` but the capability only lists `"main"`, the permission is denied.
### Permission Naming Convention
```
<plugin-name>:default -- Safe default set
<plugin-name>:allow-<command> -- Allow specific command
<plugin-name>:deny-<command> -- Deny specific command (overrides allow)
<plugin-name>:<custom-set> -- Custom permission set
```
---
## Section 2: Custom Command Permissions
### Symptoms
- Custom `#[tauri::command]` returns "not allowed"
- Command works without any capability files (in early development) but fails after adding capabilities
### Debugging Workflow
**Step 2.1**: Create a permission file in `src-tauri/permissions/` (TOML format ONLY):
```toml
# src-tauri/permissions/commands.toml
[[permission]]
identifier = "allow-my-command"
description = "Allow the my_command function"
commands.allow = ["my_command"]
```
**Step 2.2**: Reference the permission in a capability file (NO plugin prefix):
```json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"allow-my-command"
]
}
```
**Step 2.3**: Verify the command name in the permission file matches the Rust function name exactly (snake_case):
```rust
#[tauri::command]
fn get_user_data() { }
// Permission must reference: "get_user_data"
```
### Critical Rules
**ALWAYS** use TOML format for application permission files. JSON is only valid for plugin permissions.
**NEVER** add a plugin prefix to custom command permissions. Custom commands use bare identifiers like `"allow-my-command"`, NOT `"app:allow-my-command"`.
---
## Section 3: Core Permissions
### Symptoms
- Window management operations fail silently or throw errors
- Event system calls fail
- Path resolution functions fail
### Core Permission Reference
Core features require explicit permissions in v2. These are NOT granted automatically:
```json
{
"permissions": [
"core:default",
"core:window:default",
"core:window:allow-set-title",
"core:window:allow-close",
"core:window:allow-minimize",
"core:window:allow-maximize",
"core:event:default",
"core:path:default",
"core:app:default",
"core:resources:default",
"core:menu:default",
"core:tray:default"
]
}
```
**ALWAYS** include `core:default` as a minimum. It covers basic app functionality. Add specific `core:<feature>:default` or `core:<feature>:allow-<action>` as needed.
**NEVER** assume core features work without permissions. Unlike Tauri v1 which had an allowlist, v2 denies everything not explicitly permitted.
---
## Section 4: CSP Violations
### Symptoms
- Browser console: `Refused to load the script ...`
- Browser console: `Refused to connect to ...`
- Browser console: `Refused to load the image ...`
- External API calls fail, images do not load, scripts blocked
### Debugging Workflow
**Step 4.1**: Check the CSP in `src-tauri/tauri.conf.json`:
```json
{
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost; connect-src ipc: http://ipc.localhost"
}
}
}
```
**Step 4.2**: Map the console error to the correct CSP directive:
| Error Message Contains | CSP Directive to Modify | Example Addition |
|----------------------|------------------------|------------------|
| "Refused to load the script" | `script-src` | `script-src 'self'` |
| "Refused to connect to" | `connect-src` | `connect-src ipc: http://ipc.localhost https://api.example.com` |
| "Refused to load the image" | `img-src` | `img-src 'self' asset: https://asset.localhost data:` |
| "Refused to load the font" | `font-src` | `font-src 'self' data:` |
| "Refused to load the stylesheet" | `style-src` | `style-src 'self' 'unsafe-inline'` |
| "Refused to load media" | `media-src` | `media-src 'self' asset: https://asset.localhost` |
**Step 4.3**: Verify Tauri-specific protocols are included:
| Protocol | Purpose | Required In |
|----------|---------|-------------|
| `ipc:` / `http://ipc.localhost` | IPC communication | `connect-src` (ALWAYS required) |
| `asset:` / `https://asset.localhost` | Bundled assets | `img-src`, `media-src` |
| `'self'` | App's own origin | `default-src` (ALWAYS required) |
**Step 4.4**: Common working CSP for most Tauri apps:
```
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost data:; font-src 'self' data:; connect-src ipc: http://ipc.localhost
```
### Critical Rules
**NEVER** set CSP to `null` in production. This disables all content security restrictions.
**NEVER** use `dangerousDisableAssetCspModification: true` unless you fully understand the implications.
**ALWAYS** include `ipc:` and `http://ipc.localhost` in `connect-src`. Without this, IPC communication fails entirely.
**ALWAYS** enable `freezePrototype: true` in production to prevent prototype pollution attacks:
```json
{
"app": {
"security": {
"freezePrototype": true
}
}
}
```
---
## Section 5: Scope Violations
### Symptoms
- File operations fail even with `fs:allow-read-file`
- HTTP requests fail even with `http:default`
- Error mentions "scope" or path is outside allowed scope
### Debugging Workflow
**Step 5.1**: Check if the permission has scope restrictions. Add explicit scopes to capability permissions:
```json
{
"permissions": [
{
"identifier": "fs:allow-read-file",
"allow": [{ "path": "$APPDATA/**" }, { "path": "$RESOURCE/**" }],
"deny": [{ "path": "$APPDATA/secrets/**" }]
},
{
"identifier": "http:default",
"allow": [{ "url": "https://api.example.com/*" }],
"deny": [{ "url": "https://api.example.com/admin/*" }]
}
]
}
```
**Step 5.2**: Verify path variables resolve correctly:
| Variable | Resolves To |
|----------|-------------|
| `$APPDATA` | App-specific data directory |
| `$APPCONFIG` | App-specific config directory |
| `$APPLOCALDATA` | App-specific local data |
| `$APPCACHE` | App-specific cache |
| `$APPLOG` | App-specific log directory |
| `$RESOURCE` | Bundled resource directory |
| `$HOME` | User home directory |
| `$DESKTOP` | Desktop directory |
| `$DOCUMENT` | Documents directory |
| `$DOWNLOAD` | Downloads directory |
**Step 5.3**: Remember that deny rules ALWAYS take precedence over allow rules. If both match, the operation is denied.
**Step 5.4**: For filesystem operations, verify paths are relative to a `BaseDirectory` in the frontend code:
```typescript
// WRONG: absolute path, will fail scope check
await readTextFile('/etc/passwd');
// CORRECT: relative to base directory
await readTextFile('config.json', { baseDir: BaseDirectory.AppConfig });
```
---
## Section 6: Silent Permission Failures
### Symptoms
- No error thrown, but feature does not work
- Events are not received
- Window operations have no visible effect
### Debugging Steps
**Step 6.1**: Check the Rust-side console/logs for permission warnings. Tauri may log permission denials without propagating them to the frontend.
**Step 6.2**: Verify the window label matches. If a capability targets `"main"` but the active window is `"settings"`, permissions do not apply:
```json
{
"windows": ["main", "settings"],
"permissions": ["core:event:default"]
}
```
**Step 6.3**: Verify capability files are in the correct location. Files in `src-tauri/capabilities/` are auto-loaded. Files elsewhere are ignored.
**Step 6.4**: Check for conflicting capabilities. Multiple capabilities for the same window are merged additively. There is no override -- permissions accumulate.
---
## Section 7: Configuration Format Errors
### Symptoms
- Build errors mentioning capability or permission parsing
- `cargo build` fails with schema validation errors
- Permission identifiers not recognized
### Debugging Steps
**Step 7.1**: Verify file format rules:
| File Type | Location | Format | Extension |
|-----------|----------|--------|-----------|
| Capability files | `src-tauri/capabilities/` | JSON or TOML | `.json` or `.toml` |
| App permission files | `src-tauri/permissions/` | TOML only | `.toml` |
| Plugin permission files | Inside plugin crate | JSON or TOML | `.json` or `.toml` |
**Step 7.2**: Verify the `$schema` reference points to the correct generated schema:
```json
{
"$schema": "../gen/schemas/desktop-schema.json"
}
```
Run `cargo build` once to generate schemas in `src-tauri/gen/schemas/`.
**Step 7.3**: Verify permission identifiers are lowercase ASCII only (`[a-z]`, hyphens, colons). Maximum 116 characters.
**Step 7.4**: Verify inline capabilities in `tauri.conf.json` use the correct structure:
```json
{
"app": {
"security": {
"capabilities": [
{
"identifier": "inline-cap",
"windows": ["*"],
"permissions": ["fs:default"]
},
"external-capability-file-name"
]
}
}
}
```
---
## Step-by-Step Permission Setup Workflow
For any new plugin or custom command, follow this checklist:
1. **Install**: Add Cargo crate and npm package
2. **Register**: Call `.plugin()` in Rust Builder
3. **Create capability** (if none exists): Create `src-tauri/capabilities/default.json`
4. **Add permission**: Add `"<plugin>:default"` or specific `"<plugin>:allow-<cmd>"` to the permissions array
5. **Set window scope**: Ensure the `"windows"` array includes the window that will call the API
6. **Add scopes** (if needed): Configure allow/deny paths or URLs
7. **Rebuild**: Run `cargo build` to regenerate schemas
8. **Test**: Verify the operation works in both dev and production builds
---
## Reference Links
- [references/methods.md](references/methods.md) -- Permission system API reference
- [references/examples.md](references/examples.md) -- Complete capability file examples
- [references/anti-patterns.md](references/anti-patterns.md) -- Permission configuration mistakes
### Official Sources
- https://v2.tauri.app/security/permissions/
- https://v2.tauri.app/security/capabilities/
- https://v2.tauri.app/reference/config/#securityconfig