coollabsio/coolify · error · RuntimeException

The directory backup workdir is unavailable.

Error message

The directory backup workdir is unavailable.

What it means

Thrown by ScheduledVolumeBackup::sourcePath() (app/Models/ScheduledVolumeBackup.php:178). For a directory LocalFileVolume whose fs_path is relative (starts with '.'), the real path is workdir-relative: sourcePath() must call workdir() on the owning resource obtained via targetResource() ($this->backupable?->resource). If that resource is null — the storage row outlived its application/service/database — or the resource class does not have a workdir() method, the path cannot be resolved and the RuntimeException fires.

Source

Thrown at app/Models/ScheduledVolumeBackup.php:178

    }

    public function sourcePath(): string
    {
        $target = $this->backupable;

        if ($target instanceof LocalPersistentVolume) {
            return filled($target->host_path) ? $target->host_path : $target->name;
        }

        if (! $target instanceof LocalFileVolume || ! $target->is_directory) {
            throw new \RuntimeException('The backup target is not a directory or persistent volume.');
        }

        $path = str($target->fs_path);
        if ($path->startsWith('.')) {
            $resource = $this->targetResource();
            if (! $resource || ! method_exists($resource, 'workdir')) {
                throw new \RuntimeException('The directory backup workdir is unavailable.');
            }

            return $resource->workdir().$path->after('.')->toString();
        }

        return $path->toString();
    }
}

View on GitHub (pinned to 70b9acc424)

Solutions

  1. Restore or re-create the owning resource so backupable->resource resolves, or re-attach the storage to a live resource.
  2. Prefer an absolute fs_path (e.g. /data/... ) for directory storages that get backup schedules — sourcePath() returns it directly without needing workdir().
  3. If the resource is gone for good, delete the orphaned storage and its schedule instead of leaving a backup that always fails.

Example fix

// before
$path = $backup->sourcePath(); // throws: relative dir path, owning resource gone

// after — ensure an owning resource exists, or store an absolute path
$resource = $backup->targetResource();
if ($resource === null || ! method_exists($resource, 'workdir')) {
    // orphaned: clean up rather than retry forever
    $backup->delete();
    return;
}
$path = $backup->sourcePath();

// structural fix: use an absolute directory path when creating the storage
$fileVolume->fs_path = '/data/backups/dir'; // instead of './dir'
Defensive patterns

Strategy: type-guard

Validate before calling

// Before relying on sourcePath(), ensure a relative directory path has a live owner with workdir()
$target = $backup->backupable;
if ($target instanceof \App\Models\LocalFileVolume && str($target->fs_path)->startsWith('.')) {
    $resource = $target->resource;
    if ($resource === null || ! method_exists($resource, 'workdir')) {
        abort(422, 'Directory backup target has no owning resource — re-attach it or use an absolute path.');
    }
}
$path = $backup->sourcePath();

Type guard

function hasResolvableWorkdir(\App\Models\ScheduledVolumeBackup $backup): bool
{
    $target = $backup->backupable;
    if (! ($target instanceof \App\Models\LocalFileVolume)) {
        return true; // volumes resolve via host_path/name
    }
    if (! str($target->fs_path)->startsWith('.')) {
        return true; // absolute path needs no workdir
    }
    $resource = $target->resource;

    return $resource !== null && method_exists($resource, 'workdir');
}

Try / catch

try {
    $path = $backup->sourcePath();
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'workdir is unavailable')) {
        // owning resource deleted or lacks workdir(): delete the orphaned schedule or re-attach storage to a live resource
        $backup->delete();
        return;
    }
    throw $e;
}

Prevention

When it happens

Trigger: Running a directory backup where the LocalFileVolume's resource (application, service application, service database) was deleted but the volume/schedule rows remained, or the morphed resource type is a model without a workdir() method; combined with a relative fs_path like './data'.

Common situations: Partial cascades that delete the resource but leave file-storage rows; schedules attached to storages of exotic/custom resource types; data imported between instances where resource rows are missing.

Related errors


AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17). Data as JSON: /api/errors/af888869ce9f4ef1. Report an issue: GitHub.