charmbracelet/crush · error

permission request failed

Error message

permission request failed: %w

What it means

This error is returned when the tool's permission request to the configured permission service fails (the Request call itself returned an error, e.g. transport/handler failure), distinct from a denial which returns NewPermissionDeniedResponse(). The wrapped error indicates the permission workflow could not complete, so the file replacement is aborted before any write.

Solutions

  1. Inspect the wrapped error to identify the permission provider failure.
  2. Attach a working permission handler/approver for the session (interactive UI or configured auto-allow rules).
  3. Add an allow-list entry for the edit permission in crush config so no interactive request is needed.
  4. Retry the operation once a permission handler is available.

Example fix

// before
// no config; every edit triggers a permission request that fails headless
// after
// crushrc
permissions {
	allow ["edit"]
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure a permission handler exists before running the tool
if perms == nil || !perms.HasHandler() {
	return errors.New("no permission handler attached for this session")
}

Try / catch

resp, err := tool.Run(ctx, call)
if err != nil && strings.Contains(err.Error(), "permission request failed") {
	// fall back to auto-allowed config or surface to user
	return handlePermissionFailure(err)
}

Prevention

When it happens

Trigger: Calling lsp_replace_symbol on a file requiring approval when the permissions.Request call errors: permission provider misconfigured, the UI/handler session is unavailable or closed, or the permission service's underlying request channel fails.

Common situations: Running headless where no permission approver is attached; the session was cancelled while the prompt was pending; a custom permission provider returned an error; permission timeouts in non-interactive environments.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/ed35d6b6a5c8f8e4. Report an issue: GitHub.

Appendix: source

Thrown at internal/agent/tools/lsp_replace_symbol.go:147

			}

			newContent := strings.Join(newLines, "\n")

			sessionID := GetSessionFromContext(ctx)
			if sessionID != "" && permissions != nil {
				granted, err := permissions.Request(ctx, permission.CreatePermissionRequest{
					SessionID:   sessionID,
					Path:        params.FilePath,
					ToolName:    ReplaceSymbolToolName,
					Description: fmt.Sprintf("%s symbol '%s' in %s", action, params.Symbol, params.FilePath),
					Params: ReplaceSymbolPermissionsParams{
						FilePath:   params.FilePath,
						OldContent: string(content),
						NewContent: newContent,
					},
				})
				if err != nil {
					return fantasy.ToolResponse{}, fmt.Errorf("permission request failed: %w", err)
				}
				if !granted {
					return NewPermissionDeniedResponse(), nil
				}
			}

			if files != nil && sessionID != "" {
				if _, err := files.CreateVersion(ctx, sessionID, params.FilePath, string(content)); err != nil {
					slog.Warn("Failed to create file version before replace", "path", params.FilePath, "error", err)
				}
			}

			if err := os.WriteFile(params.FilePath, []byte(newContent), 0o644); err != nil {
				return fantasy.ToolResponse{}, fmt.Errorf("failed to write file: %w", err)
			}

			if filetracker != nil && sessionID != "" {
				filetracker.RecordRead(ctx, sessionID, params.FilePath)

View on GitHub (pinned to 7944b8e522)