kopia/kopia · error
unable to process directory
Error message
unable to process directory %q
What it means
When processing a child directory fails with an error that is NOT a dirReadError, processSingle treats it as fatal for the upload and wraps it with 'unable to process directory %q'. dirReadError failures (e.g. permission or IO problems reading a directory) are instead reported per policy and possibly ignored; any other failure type aborts the whole snapshot upload.
Solutions
- Read the %q name and the wrapped cause to locate the failing directory and underlying error type
- If the cause is an entry-type problem, fix or exclude that filesystem entry via ignore rules
- Verify repository storage health if the cause is an upload/write failure
- If it's a policy-action error surfacing as a non-dirReadError, correct the action script
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
info, err := os.Stat(dirPath)
if err != nil || !info.IsDir() {
return fmt.Errorf("%s is not a readable directory: %w", dirPath, err)
} Try / catch
err := uploadDir(ctx, ...)
var dre dirReadError
if errors.As(err, &dre) {
// policy-controlled: report and maybe ignore
reportError(dre.error)
} else if strings.Contains(err.Error(), "unable to process directory") {
log.Fatalf("fatal failure processing %v: %v", dirPath, err)
return err
} Prevention
- Read the %q directory name in the message to isolate the failing subtree
- Exclude problematic paths via ignore rules
- Validate repository storage health before large tree uploads
- Keep kopia up to date; non-dirReadError aborts often indicate bugs or type issues
When it happens
Trigger: processEntryUploadResult or another step for a child directory returns a non-dirReadError error during processDirectoryEntries — e.g. upload/object-write failures, checkpoint errors, or panics converted to errors while processing entry.Name().
Common situations: Storage backend failure mid-snapshot causing non-read errors while walking the tree; bug or unexpected state in entry upload results; unusual filesystem entries causing type errors (cascading from 'invalid entry type'); running out of memory during very wide directory scans.
Related errors
- error checkpointing children
- running checkpointers
- snapshot task
- unable to write directory
- unexpected entry type: %T
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/1f16e692772562e4.
Report an issue: GitHub.
Appendix: source
Thrown at snapshot/upload/upload.go:898
childTree := policyTree.Child(entry.Name())
childPrevDirs := uniqueChildDirectories(ctx, prevDirs, entry.Name())
de, err := uploadDirInternal(ctx, u, entry, childTree, childPrevDirs, childLocalDirPathOrEmpty, entryRelativePath, childDirBuilder, parentCheckpointRegistry)
if errors.Is(err, errCanceled) {
return err
}
if err != nil {
// Note: This only catches errors in subdirectories of the snapshot root, not on the snapshot
// root itself. The intention is to always fail if the top level directory can't be read,
// otherwise a meaningless, empty snapshot is created that can't be restored.
var dre dirReadError
if errors.As(err, &dre) {
isIgnoredError := childTree.EffectivePolicy().ErrorHandlingPolicy.IgnoreDirectoryErrors.OrDefault(false)
u.reportErrorAndMaybeCancel(dre.error, isIgnoredError, parentDirBuilder, entryRelativePath)
} else {
return errors.Wrapf(err, "unable to process directory %q", entry.Name())
}
} else {
parentDirBuilder.AddEntry(de)
}
return nil
case fs.Symlink:
compressor := policyTree.Child(entry.Name()).EffectivePolicy().MetadataCompressionPolicy.MetadataCompressor()
de, err := u.uploadSymlinkInternal(ctx, entryRelativePath, entry, compressor)
return u.processEntryUploadResult(ctx, de, err, entryRelativePath, parentDirBuilder,
policyTree.EffectivePolicy().ErrorHandlingPolicy.IgnoreFileErrors.OrDefault(false),
u.OverrideEntryLogDetail.OrDefault(policyTree.EffectivePolicy().LoggingPolicy.Entries.Snapshotted.OrDefault(policy.LogDetailNone)),
"snapshotted symlink", t0)
case fs.File:
atomic.AddInt32(&u.stats.NonCachedFiles, 1)View on GitHub (pinned to 82495e54b5)