argoproj/argo-workflows · error
unable to open file %s: %w
Error message
unable to open file %s: %w
What it means
PutFile opens the local file at `path` with os.Open before uploading to Azure. If the file cannot be opened (does not exist, permission denied, path is a broken symlink), the OS error is wrapped with this message. This is a local-filesystem failure, not an Azure failure.
Source
Thrown at workflow/artifacts/azure/azure.go:333
containerClient, err := azblobDriver.newAzureContainerClient(ctx)
if err != nil {
return fmt.Errorf("unable to create Azure Blob Container client for %s: %w", outputArtifact.Azure.Blob, err)
}
blobClient := containerClient.NewBlockBlobClient(outputArtifact.Azure.Blob)
if _, err = blobClient.UploadStream(ctx, reader, nil); err != nil {
return fmt.Errorf("unable to upload stream to Azure blob %s: %w", outputArtifact.Azure.Blob, err)
}
return nil
}
// PutFile uploads a file to Azure Blob Storage
func PutFile(ctx context.Context, containerClient *container.Client, blobName, path string) error {
blobClient := containerClient.NewBlockBlobClient(blobName)
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("unable to open file %s: %w", path, err)
}
defer func() {
if closeErr := file.Close(); closeErr != nil {
logger := logging.RequireLoggerFromContext(ctx)
logger.WithError(closeErr).Warn(ctx, "unable to close file")
}
}()
_, err = blobClient.UploadFile(ctx, file, nil)
return err
}
// PutDirectory uploads all files in a directory to Azure Blob Storage
func PutDirectory(ctx context.Context, containerClient *container.Client, blobName, path string) error {
for putTask := range generatePutTasks(blobName, path) {
err := PutFile(ctx, containerClient, putTask.blobName, putTask.path)
if err != nil {
return errView on GitHub (pinned to 35bff19146)
Solutions
- Confirm the file exists at the exact path (ls -l) inside the workflow container.
- Fix file permissions/ownership so the executor user can read it.
- Ensure no concurrent step deletes or moves the file before artifact save.
- Check for broken symlinks in directory artifacts.
- Use absolute paths and keep artifact paths inside the mounted volume.
Example fix
// before path: /work/out.txt // file deleted by a cleanup step // after: keep until after save main: generate → save artifact → cleanup container
Defensive patterns
Strategy: validation
Validate before calling
f, err := os.Open(path)
if err != nil { return err }
f.Close()
// safe to call PutFile now Type guard
func readableFile(p string) bool {
f, err := os.Open(p)
if err != nil { return false }
f.Close()
return true
} Try / catch
err := PutFile(ctx, containerClient, blobName, path)
if err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrNotExist) {
// local file vanished — check producing step and cleanup ordering
}
return err
} Prevention
- Verify file existence/permissions before artifact save in the template.
- Order cleanup steps after artifact save in the pod lifecycle.
- Avoid broken symlinks in directory artifacts.
- Run workflow containers with a user that can read all output files.
When it happens
Trigger: PutFile called by Save/PutDirectory with a path that vanished or is unreadable: file removed during the workflow, wrong path passed, permission restrictions for the executor process.
Common situations: Race where a cleanup step deletes files before save; running as non-root while file is 0600 root-owned; symlink to a missing target; directory artifact where a child path was removed mid-walk.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- mkdir %s error: %w
- failed to create folder path: %w
- failed to write large arg %d to file: %w
- failed to leave working directory before staging input artif
- failed to create parent directory for artifact %q at %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/67e2e889f53353fd.
Report an issue: GitHub.