Azure Pipelines: "Access to the path '/home/vsts' is denied" on Ubuntu 26.04
On Microsoft-hosted Ubuntu agents up to 24.04, the agent runs as the user vsts and the job's folder is /home/vsts/work/1. On the Ubuntu 26.04 image it runs as runner, the job's folder is /home/runner/work/1, and /home/vsts does not exist (runner-images #15028, open). /home belongs to root, so a step that creates or writes anything under /home/vsts is refused. Microsoft's page of predefined variables still gives /home/vsts/work/1 and /home/vsts/work/_temp as its examples, which is where many of these paths came from.
Why it starts without a change to the pipeline
Microsoft's page of hosted agents (updated October 6) still maps ubuntu-latest to 24.04, and that image is the default when a YAML pipeline names none. But the move of ubuntu-latest to 26.04, rolled out from October 19 to November 19, 2026, lists Azure DevOps among the affected platforms (runner-images #14748), and pipelines are already landing on it: one with vmImage: 'ubuntu-latest' ran on 26.04 image 20261012.163.1 on October 26, and one that names no vmImage ran on 20261025.179.1 on November 2, as runner, after its last good run on 24.04 as vsts. Until the move ends, a re-run can land on 24.04 and pass.
What it looks like
The Cache@2 task, after a cache hit, when its path is under /home/vsts (a comment on runner-images #14748, on November 2):
There is a cache hit: ... ##[error]Access to the path '/home/vsts' is denied.
A script in a build validation pipeline that creates the sources folder itself (runner-images #15105):
fatal: cannot mkdir /home/vsts/work/1/s: Permission denied
A task input set to a fixed folder: HawkScan's installPath: '/home/vsts/custom' stopped a release build (javaspringvulny #111). Search the repository for /home/vsts: YAML, scripts, Docker volume mounts and config files.
Fix: build the path from a variable
- The sources:
$(Build.SourcesDirectory), or$BUILD_SOURCESDIRECTORYin a script. The runner-images maintainers' answer to thegit initcase isgit init "$env:BUILD_SOURCESDIRECTORY". - The job's folder:
$(Pipeline.Workspace), or$PIPELINE_WORKSPACE. - Scratch space for tools and downloads:
$(Agent.TempDirectory), emptied after each job. The HawkScan fix uses$(Agent.TempDirectory)/hawk-custom. - The user's home:
$HOMEin bash,$env:HOMEin PowerShell. A task input cannot read it, so set a variable in a script step first:
steps:
- bash: echo "##vso[task.setvariable variable=userHome]$HOME"
displayName: Find the agent user's home
- task: Cache@2
inputs:
key: 'deps | "$(Agent.OS)" | Dependencies.toml'
path: $(userHome)/.ballerina/repositories
Microsoft's caching examples avoid the home folder altogether: they point the tool's cache at $(Pipeline.Workspace), for example npm_config_cache: $(Pipeline.Workspace)/.npm or GRADLE_USER_HOME: $(Pipeline.Workspace)/.gradle, and cache that path, which works the same on both images. If the cached files hold absolute paths (some tools write their home into them), change the key once so that 26.04 builds its own cache.
Or keep the old image for now
pool: vmImage: 'ubuntu-24.04'
The maintainers suggest this to keep the old layout. Naming the image also stops the job from switching between 24.04 and 26.04 from one run to the next, but it only buys time: move the paths to variables before you go to 26.04.
Telling it apart
- The Initialize job step of the log shows the image and its version, for example
Image: ubuntu-26.04. whoamiprintsrunner, andecho $HOMEprints/home/runner.- Self-hosted agents are not part of this: their user is the one the agent was set up with.
Another 26.04 change: conda
The 26.04 image has no Miniconda, and CONDA is empty, so a pipeline that follows Microsoft's Anaconda guide and adds $CONDA/bin to the path fails with "conda: command not found" and exit code 127: install Miniforge in a step, or keep vmImage: ubuntu-24.04.
Build validation in IntelliJ IDEA and Rider
Azure DevOps Pull Requests, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists your pull requests in an IDE tool window with their branch policies in plain words, so a build policy that started failing during the move shows up as failed next to the pull request. With Pro, a build that failed or expired has a Run again link that queues it again. The review inbox and the branch policies are free; voting, commenting, completing, running a build policy again and creating pull requests are Pro (US$1 a month, with a 30-day trial).
Get it on JetBrains Marketplace
Azure DevOps is a trademark of Microsoft Corporation; Ubuntu is a trademark of Canonical Ltd.; IntelliJ IDEA and Rider are trademarks of JetBrains s.r.o. This page is not affiliated with or endorsed by Microsoft, Canonical or JetBrains.
More: build validation: expired builds and why a pull request build did not run · "fatal: could not read Username for 'https://dev.azure.com'" · the same move on GitHub Actions · 日本語