charmbracelet/crush · error

permission request failed: %w

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.

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)

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.