Skip to content

The system clipboard silently fails to be read, via all Wayland compositors. #26832

Open

Description

Prerequisites

Steps to reproduce

This was originally reported at discussions/26745, where the reproduction method was more complex. However, I have confirmed that the undermentioned is sufficient, which Metekillot (@Metekillot) corroborates:

#!/usr/bin/env pwsh
#Requires -PSEdition Core
#Requires -Version 7.5.4
If ($Env:XDG_CURRENT_DESKTOP -Eq 'KDE') {
	wl-copy 'Placeholder' && `
	wl-paste && `
	Get-Clipboard
}

You should not utilise Start-Transcript to record this, as issues/26833 explains.

Expected behavior

PS /> wl-copy 'Placeholder'
PS /> wl-paste             
Placeholder
PS /> Get-Clipboard        
Placeholder
PS />

Actual behavior

PS /> wl-copy 'Placeholder'
PS /> wl-paste             
Placeholder
PS /> Get-Clipboard        

PS />

Error details

PS /> $(Get-Error -Newest 1) -Eq $Null
True

Environment data

PSVersion                 : 7.5.4
PSEdition                 : Core
GitCommitId               : 7.5.4
OS                        : Fedora Linux 43 (KDE Plasma Desktop Edition)
Platform                  : Unix
PSCompatibleVersions      : {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion : 2.3
SerializationVersion      : 1.1.0.1
WSManStackVersion         : 3.0

Visuals

Image

Activity

  1. 237dmitry commented on Feb 15, 2026

    @237dmitry

    I wonder what utility the *-Clipboard cmdlets use? The help says it's xclip, but why do them work on a TTY (without X11)? And what should be used on Wayland?

  2. beedell-roke commented on Feb 15, 2026

    @beedell-roke
    Author

    I wonder what utility the *-Clipboard cmdlets use?

    237dmitry, blob/08b13f24e6d359877b2cdbe650f7c8fc5ca20135/src/Microsoft.PowerShell.Commands.Management/commands/management/GetClipboardCommand.cs defines it:

    // Copyright (c) Microsoft Corporation.
    // Licensed under the MIT License.
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Management.Automation;
    using System.Management.Automation.Language;
    using Microsoft.PowerShell.Commands.Internal;
    namespace Microsoft.PowerShell.Commands
    {
    /// <summary>
    /// Defines the implementation of the 'Get-Clipboard' cmdlet.
    /// This cmdlet get the content from system clipboard.
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "Clipboard", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2109905")]
    [Alias("gcb")]
    [OutputType(typeof(string))]
    public class GetClipboardCommand : PSCmdlet
    {
    /// <summary>
    /// Property that sets raw parameter. This will allow clipboard return text or file list as one string.
    /// </summary>
    [Parameter]
    public SwitchParameter Raw
    {
    get
    {
    return _raw;
    }
    set
    {
    _raw = value;
    }
    }
    /// <summary>
    /// Gets or sets the delimiters to use when splitting the clipboard content.
    /// </summary>
    [Parameter]
    [ArgumentCompleter(typeof(DelimiterCompleter))]
    public string[] Delimiter { get; set; } = [Environment.NewLine];
    private bool _raw;
    /// <summary>
    /// This method implements the ProcessRecord method for Get-Clipboard command.
    /// </summary>
    protected override void BeginProcessing()
    {
    this.WriteObject(GetClipboardContentAsText(), true);
    }
    /// <summary>
    /// Returns the clipboard content as text format.
    /// </summary>
    /// <returns>Array of strings representing content from clipboard.</returns>
    private List<string> GetClipboardContentAsText()
    {
    var result = new List<string>();
    string textContent = null;
    try
    {
    textContent = Clipboard.GetText();
    }
    catch (PlatformNotSupportedException)
    {
    ThrowTerminatingError(new ErrorRecord(new InvalidOperationException(ClipboardResources.UnsupportedPlatform), "FailedToGetClipboardUnsupportedPlatform", ErrorCategory.InvalidOperation, "Clipboard"));
    }
    if (_raw)
    {
    result.Add(textContent);
    }
    else
    {
    result.AddRange(textContent.Split(Delimiter, StringSplitOptions.None));
    }
    return result;
    }
    }
    /// <summary>
    /// Provides argument completion for the Delimiter parameter.
    /// </summary>
    public sealed class DelimiterCompleter : IArgumentCompleter
    {
    /// <summary>
    /// Provides argument completion for the Delimiter parameter.
    /// </summary>
    /// <param name="commandName">The name of the command that is being completed.</param>
    /// <param name="parameterName">The name of the parameter that is being completed.</param>
    /// <param name="wordToComplete">The input text to filter the results by.</param>
    /// <param name="commandAst">The ast of the command that triggered the completion.</param>
    /// <param name="fakeBoundParameters">The parameters bound to the command.</param>
    /// <returns>Completion results.</returns>
    public IEnumerable<CompletionResult> CompleteArgument(string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters)
    {
    wordToComplete ??= string.Empty;
    var pattern = new WildcardPattern(wordToComplete + '*', WildcardOptions.IgnoreCase);
    if (pattern.IsMatch("CRLF") || pattern.IsMatch("Windows"))
    {
    yield return new CompletionResult("\"`r`n\"", "CRLF", CompletionResultType.ParameterValue, "Windows (CRLF)");
    }
    if (pattern.IsMatch("LF") || pattern.IsMatch("Unix") || pattern.IsMatch("Linux"))
    {
    yield return new CompletionResult("\"`n\"", "LF", CompletionResultType.ParameterValue, "UNIX (LF)");
    }
    }
    }
    }

    However, I do not know where GetText is defined, nor why PlatformNotSupportedException is not raised, at the least.

  3. mklement0 commented on Feb 15, 2026

    @mklement0
    Contributor

    shows that xclip is used on Linux, and pbpaste on macOS.

  4. changed the title [-]7.5.4 cannot read the clipboard.[/-] [+]7.5.4 cannot read the clipboard on Wayland.[/+] on Feb 15, 2026
  5. beedell-roke commented on Feb 15, 2026

    @beedell-roke
    Author

    Michael Klement (@mklement0), Get-Command xclip raises:

    PS /home/RokeJulianLockhart> Get-Command xclip
    Get-Command: The term 'xclip' is not recognized as a name of a cmdlet, function, script file, or executable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    PS /home/RokeJulianLockhart> Get-Error -Newest 1
    
    Exception             : 
        Type        : System.Management.Automation.CommandNotFoundException
        ErrorRecord : 
            Exception             : 
                Type    : System.Management.Automation.ParentContainsErrorRecordException
                Message : The term 'xclip' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
                HResult : -2146233087
            TargetObject          : xclip
            CategoryInfo          : ObjectNotFound: (xclip:String) [], ParentContainsErrorRecordException
            FullyQualifiedErrorId : CommandNotFoundException
        CommandName : xclip
        Message     : The term 'xclip' is not recognized as a name of a cmdlet, function, script file, or executable program.
                      Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
        HResult     : -2146233087
    TargetObject          : xclip
    CategoryInfo          : ObjectNotFound: (xclip:String) [Get-Command], CommandNotFoundException
    FullyQualifiedErrorId : CommandNotFoundException,Microsoft.PowerShell.Commands.GetCommandCommand
    InvocationInfo        : 
        MyCommand        : Get-Command
        ScriptLineNumber : 1
        OffsetInLine     : 1
        HistoryId        : 1
        Line             : Get-Command xclip
        Statement        : Get-Command xclip
        PositionMessage  : At line:1 char:1
                           + Get-Command xclip
                           + ~~~~~~~~~~~~~~~~~
        InvocationName   : Get-Command
        CommandOrigin    : Internal
    ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
    PipelineIterationInfo : 
          0
          1

    Consequently, why does calling Get-Clipboard not raise an error? My $ErrorActionPreference is not SilentlyContinue. It's Continue.

  6. mklement0 commented on Feb 15, 2026

    @mklement0
    Contributor

    If xclip isn't available, PowerShell falls back to an internal, PowerShell-only clipboard.

    That is, with xclip not found to be installed on a Linux distro, Set-Clipboard foo && Get-Clipboard works, but only because Set-Clipboard was the command to fill the PowerShell-only clipboard.

    Conversely, any clipboard provided by any other external utility will not be seen by Get-Clipboard.

  7. beedell-roke commented on Feb 15, 2026

    @beedell-roke
    Author

    Michael Klement (@mklement0), might I create a separate FR to modify that behaviour? I don't see a use case for it, without, at the least, informing the user that the system clipboard is unavailable.

  8. mklement0 commented on Feb 15, 2026

    @mklement0
    Contributor

    The use case is that as long as it is Set-Clipboard that's filling the clipboard, Get-Clipboard will work as expected.

    That said: Yes, it's worth creating a feature request.

    My hunch is that, in the interest of maintaining backward compatibility, the most realistic solution is to require -Verbose in order to be informed of the unavailability of PowerShell-transcendent functionality.

  9. beedell-roke commented on Feb 16, 2026

    @beedell-roke
    Author
  10. brianary commented on Mar 21, 2026

    @brianary
    Contributor

    GNOME 50 is out and removes X11 entirely in favor of Wayland, which is going to affect most linux distributions soon, so a Wayland fallback is pretty important. In fact, we're moving toward a situation where Wayland should probably be the primary clipboard with a fallback to X11.

    GNOME 50 debuts with X11 axed, Wayland front and center • The Register

  11. CommanderTurtle commented on Oct 20, 2026

    @CommanderTurtle

    xclip is annoying - still unfixed, unclosed PR. 'pwsh' as a linux shell hardcodes 'xclip'.

    wl-copy is needed

    Solution? Save as ~/tmp/xclip-wayland-shim.sh

    xclip-wayland-shim.sh
    #!/usr/bin/env bash
    set -euo pipefail
    
    # Install an xclip shim that forwards to wl-clipboard on Wayland, so
    # PowerShell's Set-Clipboard/Get-Clipboard (which hardcode `xclip` on
    # Linux) work in a Wayland session.
    #
    # Usage:
    #   bash ./path/to/xclip-wayland-shim.sh
    #   source ~/.bashrc
    
    target_user="${SUDO_USER:-${USER:-$(id -un)}}"
    if command -v getent >/dev/null 2>&1; then
      target_home="$(getent passwd "$target_user" | cut -d: -f6 || true)"
    else
      target_home="${HOME:?HOME is not set and getent is unavailable}"
    fi
    if [[ -z "$target_home" || ! -d "$target_home" ]]; then
      printf 'Could not resolve a home directory for %s.\n' "$target_user" >&2
      exit 1
    fi
    
    bin_dir="$target_home/.local/bin"
    shim="$bin_dir/xclip"
    bashrc="$target_home/.bashrc"
    
    mkdir -p "$bin_dir"
    
    cat > "$shim" <<'SHIM'
    #!/bin/sh
    # xclip shim: forward to wl-clipboard on Wayland, real xclip otherwise.
    # Installed by xclip-wayland-shim.sh; safe to re-run.
    
    if [ -n "${WAYLAND_DISPLAY:-}" ]; then
      case "$*" in
        *"-out"*)
          if command -v wl-paste >/dev/null 2>&1; then
            exec wl-paste
          fi
          ;;
        *)
          if command -v wl-copy >/dev/null 2>&1; then
            exec wl-copy
          fi
          ;;
      esac
    fi
    
    # Fall back to the real xclip. Absolute paths avoid re-entering this shim.
    for candidate in /usr/bin/xclip /bin/xclip; do
      if [ -x "$candidate" ]; then
        exec "$candidate" "$@"
      fi
    done
    
    printf 'xclip shim: no clipboard backend found (install wl-clipboard for Wayland, xclip for X11)\n' >&2
    exit 1
    SHIM
    
    chmod +x "$shim"
    
    path_line="export PATH=\"$bin_dir:\$PATH\""
    touch "$bashrc"
    if ! grep -Fqx -- "$path_line" "$bashrc"; then
      printf '\n# xclip Wayland shim\n%s\n' "$path_line" >> "$bashrc"
      printf 'Added to %s: %s\n' "$bashrc" "$path_line"
    else
      printf 'Bash PATH already contains the shim entry in %s.\n' "$bashrc"
    fi
    
    if [[ $(id -u) -eq 0 && "$target_user" != root ]]; then
      chown "$target_user":"$(id -gn "$target_user")" "$shim" "$bashrc"
    fi
    
    export PATH="$bin_dir:$PATH"
    resolved="$(command -v xclip || true)"
    if [[ -z "$resolved" ]]; then
      printf 'xclip not found on PATH after install.\n' >&2
      exit 1
    fi
    
    printf 'xclip shim: %s\n' "$shim"
    printf 'xclip resolves to: %s\n' "$resolved"
    printf 'This script refreshed PATH for itself. Run: source %q\n' "$bashrc"
    printf 'pwsh Set-Clipboard/Get-Clipboard now work on Wayland (needs wl-clipboard).\n'

    Then run: bash ./xclip-wayland-shim.sh & source ~/.bashrc

    Set-Clipboard is otherwise a dead, non-erroring cmdlet.

    This fixes so stdin & out work under xclip without using anything but a fancy shim.


    Other solution, i.e. Practical options on Wayland:

    1. Just use the native tools, even from pwsh: Get-Content file | wl-copy / wl-paste.
    2. If you're on X11 (or XWayland with $DISPLAY set), pacman -S xclip makes the cmdlets work.
    3. (the above fix) A fun hack that exploits the hardcoded binary name: shadow xclip with a shim earlier on your PATH that translates to wl-clipboard on Wayland:
    #!/bin/sh
    # /usr/local/bin/xclip — Wayland shim
    if [ -n "$WAYLAND_DISPLAY" ]; then
      case "$*" in
        *"-out"*) exec wl-paste ;;
        *)        exec wl-copy  ;;
      esac
    fi
    exec /usr/bin/xclip "$@"

    That covers exactly the two call patterns pwsh uses (-selection clipboard -in / -out), and both read/write stdin/stdout just like wl-copy/wl-paste do.

    So the read is correct: as of 7.6.6, Set-Clipboard on a Wayland-only system is functionally dead — it's an xclip-only implementation with a silent in-memory fallback, and Wayland support is still an open gap upstream.

    picture of working shim Image
  12. brianary commented on Oct 21, 2026

    @brianary
    Contributor

    KDE has now followed GNOME in switching to Wayland. This will soon represent the vast majority of linux systems.

    KDE turns 30 with Plasma 6.8, remote upgrades, and an X11 farewell - Ars Technica

  13. 1 remaining item

  14. changed the title [-]7.5.4 cannot read the clipboard on Wayland.[/-] [+]The system clipboard silently fails to be read, via all Wayland compositors.[/+] on Oct 21, 2026
  15. SteveL-MSFT commented on Nov 4, 2026

    @SteveL-MSFT
    Member

    The @PowerShell/wg-powershell-cmdlets discussed this. We agree that for Linux, the clipboard cmdlets should use the Wayland tools if xclip is not available.

  16. added
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    WG-ReviewedA Working Group has reviewed this and made a recommendation
    and removed
    WG-NeedsReviewNeeds a review by the labeled Working Group
    on Nov 4, 2026
  17. beedell-roke commented on Nov 4, 2026

    @beedell-roke
    Author

    if xclip is not available

    Steve Lee (@SteveL-MSFT), would XClip function with a non-X{Org} compositor, even if it were in $Env:PATH?

  18. CommanderTurtle commented on Nov 4, 2026

    @CommanderTurtle

    would XClip function with a non-X{Org} compositor, even if it were in $Env:PATH?

    If the shim is performed like above 😂

    Image
    Details
    Image
  19. beedell-roke commented on Nov 4, 2026

    @beedell-roke
    Author

    CommanderTurtle, yes; that's an abstraction above XClip. That's what I desire. However, what I quoted indicated that an implementation to solve this may merely check for the existence of XClip installed, rather than consider XClip's compatibility with its environment.

    BTW, best to paste text as actual text, rather than a screenshot, when feasible. Consider anyone who utilises a screen reader; OCR tools shall struggle with that.

  20. CommanderTurtle commented on Nov 4, 2026

    @CommanderTurtle

    For sure. The reply above had a full code reply inlined. Might've been overthinking it. I was just restating in case it was a simple check to see if xclip responds, since it would be much more robust to rather check for the package through the applicable package manager: pacman -Q xclip, etc. In this case though, just wanted to mention all of omarchy faces this trouble with powershell (Powershell effectively has no clipboard for all omarchy users - unless they run a shim workaround and stumbled on these very issue discussion threads).

    off-topic

    I don't see anyone using the internet from cli nowadays, but, your advice makes sense.
    The information contained within the image is in text above though in the codeblock, so, no info lost at least.
    WebAssembly build of paddle-ocr for console screen readers, if you're stumbling on this in the future from terminal emulator --> ppocr-wasm

  21. beedell-roke commented on Nov 4, 2026

    @beedell-roke
    Author

    I was just restating in case it was a simple check to see if xclip responds, since it would be much more robust to rather check for the package through the applicable package manager: pacman -Q xclip, etc.

    CommanderTurtle, hence the previous reference to $Env:PATH; Get-Command provides that.

    In this case though, just wanted to mention all of omarchy faces this trouble with powershell (Powershell effectively has no clipboard for all omarchy users - unless they run a shim workaround and stumbled on these very issue discussion threads).

    That is true for all Wayland DEs, to the same degree. No PowerShell clipboard exists for any Wayland DE.

  22. CommanderTurtle commented on Nov 5, 2026

    @CommanderTurtle

    Turns out I was wrong. A package manager check isn't enough. Lol. Omarchy's hyprland shows xclip but only wl-clip is the proved working path. After shim, xclip still responds to package manager call.

    PS /home/alienuser> $Env:PATH -split ':'
    /usr/lib/powershell-7
    /home/alienuser/.local/bin
    /home/alienuser/.bun/bin
    /home/alienuser/.cache/.bun/bin
    /opt/cuda/bin
    /home/alienuser/.local/share/mise/installs/bun/latest/bin
    /home/alienuser/.local/share/mise/installs/codex/latest/bin
    /home/alienuser/.local/share/mise/dotnet-root
    /home/alienuser/.local/share/mise/installs/gh/latest/gh_2.103.3_linux_amd64/bin
    /home/alienuser/.local/share/mise/installs/github-can1357-oh-my-pi/latest
    /home/alienuser/.local/share/mise/installs/node/26.12.1/bin
    /home/alienuser/.local/share/mise/installs/npm-playwright/latest/node_modules/.bin
    /home/alienuser/.local/share/mise/installs/python/3.15/bin
    /home/alienuser/.local/share/mise/installs/ruby/latest/bin
    /home/alienuser/.local/share/mise/shims
    /usr/share/omarchy/bin
    /home/alienuser/.cargo/bin
    /home/alienuser/.local/share/../bin
    /usr/local/sbin
    /usr/local/bin
    /usr/bin
    /opt/cuda/bin
    /usr/bin/site_perl
    /usr/bin/vendor_perl
    /usr/bin/core_perl
    PS /home/alienuser> Get-Command xclip
    
    CommandType     Name                                               Version    Source
    -----------     ----                                               -------    ------
    Application     xclip                                              0.0.0.0    /home/alienuser/.local/bin/xclip
    
    PS /home/alienuser> if (Get-Command xclip -ErrorAction SilentlyContinue) {  
    >>     "xclip is available"
    >> } else {
    >>     "xclip NOT found in PATH"
    >> }
    xclip is available
    PS /home/alienuser> Get-Command xclip -All
    
    CommandType     Name                                               Version    Source
    -----------     ----                                               -------    ------
    Application     xclip                                              0.0.0.0    /home/alienuser/.local/bin/xclip
    Application     xclip                                              0.0.0.0    /home/alienuser/.local/share/../bin/xclip
    Application     xclip                                              0.0.0.0    /usr/bin/xclip
    
    PS /home/alienuser> echo @'  
    >> A more correct route...?
    >> '@; echo @'
    >> Check for wl-clipboard, then xclip:
    >> '@; (pacman -Ql wl-clipboard 2>$null | Select-String '/bin/'); (pacman -Ql xclip 2>$null | Select-String '/bin/')       
    A more correct route...?
    Check for wl-clipboard, then xclip:
    
    wl-clipboard /usr/bin/
    wl-clipboard /usr/bin/wl-copy
    wl-clipboard /usr/bin/wl-paste
    xclip /usr/bin/
    xclip /usr/bin/xclip
    xclip /usr/bin/xclip-copyfile
    xclip /usr/bin/xclip-cutfile
    xclip /usr/bin/xclip-pastefile
    
    PS /home/alienuser> pacman -Q xclip; pacman -Q wl-clipboard
    xclip 0.13-6
    wl-clipboard 1:2.3.0-1

    Makes this even more convoluted to shim installers.
    PowerShell has no idea what your compositor is. It's all env vars and loginctl...

    Proper method is probably then a test just simply if "Set-Clipboard" modifies the clipboard. If both are installed fallover to wl-clipboard - due to xwayland being wonky to begin with.

    This is the whole reason to care which binary is real. On omarchy, xclip only works via XWayland — and it hangs forever if no X socket exists, which is a miserable failure mode in a script.

    PS /home/alienuser> function Copy-Clipboard2 {
    >>     param([Parameter(ValueFromPipeline=$true)][string]$Text)
    >>     process {
    >>         if ($Env:WAYLAND_DISPLAY) { $Text | wl-copy }
    >>         elseif ($Env:DISPLAY)     { $Text | xclip -selection clipboard }
    >>         else                      { throw 'No clipboard backend (TTY session?)' }
    >>     }
    >> }

    And to sanity-check that the winner on PATH actually functions rather than merely existing:

    PS /home/alienuser> "probe" | wl-copy; wl-paste          # Wayland
    probe
    
    PS /home/alienuser> "probe" | xclip -sel clip; xclip -o -sel clip   # X11/XWayland
    
    ^C
    PS /home/alienuser> 

    If ~/.local/bin/xclip turns out to be a shim that execs wl-clipboard (a common trick people use to make xclip scripts work on Wayland), that explains everything — and you'd want to keep it, not "fix" it by putting /usr/bin first.

    Second command apparently hangs... which I guess, is a good thing! Science succeeded.

  23. beedell-roke commented on Nov 5, 2026

    @beedell-roke
    Author

    it hangs forever if no X socket exists, which is a miserable failure mode in a script.

    CommanderTurtle, that's probably worth a PR for astrand/xclip, citing this and issues/62#issuecomment-966020595 as examples of interest in it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugNeeds-TriageThe issue is new and needs to be triaged by a work group.Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsWG-Cmdletsgeneral cmdlet issuesWG-ReviewedA Working Group has reviewed this and made a recommendation

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions