tobi/qmd · warning

⚠ Skipped file outside collection: ${skipped.file}

Error message

⚠ Skipped file outside collection: ${skipped.file}

What it means

reportSkippedReads in the CLI prints '⚠ Skipped file outside collection: {file}' for every entry in skippedFiles whose code is OUTSIDE_COLLECTION — i.e. it is the user-facing echo of the store-level skip from src/store.ts (error 60). It means the indexer refused to ingest those files because their resolved path (via glob `../`, absolute patterns, or symlinks) lies outside the collection root, and any previously indexed copies are deactivated.

Source

Thrown at src/cli/qmd.ts:2069

    console.log(`\nRun 'qmd embed' to update embeddings (${needsEmbedding} unique hashes need vectors)`);
  }

  closeDb();
}

function fsErrorCode(err: unknown): string {
  if (err && typeof err === "object" && "code" in err) {
    const code = (err as { code: unknown }).code;
    if (typeof code === "string" && code.length > 0) return code;
  }
  return "ERROR";
}

function reportSkippedReads(skippedFiles: { file: string; code: string }[]): void {
  if (skippedFiles.length === 0) return;
  for (const skipped of skippedFiles) {
    if (skipped.code === "OUTSIDE_COLLECTION") {
      console.warn(`⚠ Skipped file outside collection: ${skipped.file}`);
    } else {
      console.warn(`⚠ Skipped unreadable file: ${skipped.file} (${skipped.code})`);
    }
  }
  const escaped = skippedFiles.filter(f => f.code === "OUTSIDE_COLLECTION").length;
  const unreadable = skippedFiles.length - escaped;
  if (escaped) console.warn(`Skipped ${escaped} file(s) outside the collection root`);
  if (unreadable) console.warn(`Skipped ${unreadable} unreadable file(s)`);
}

function renderProgressBar(percent: number, width: number = 30): string {
  const filled = Math.round((percent / 100) * width);
  const empty = width - filled;
  const bar = "█".repeat(filled) + "░".repeat(empty);
  return bar;
}

function parseEmbedBatchOption(name: string, value: unknown): number | undefined {

View on GitHub (pinned to dbfd0b4736)

Solutions

  1. Fix the `--mask` so it cannot escape the collection root (no `../` segments, no absolute patterns) and re-run `qmd update`
  2. Add the outside directory as its own collection: `qmd collection add <dir> --name <n>`
  3. Resolve escaping symlinks: replace them with real files under the root, or move their targets inside the collection
  4. If the layout is intentional, re-add the collection with a higher-level root so all desired files are inside it

Example fix

# before
qmd collection add ~/notes --name notes --mask '**/*.md,../*.md'
# update prints: ⚠ Skipped file outside collection: ../todo.md

# after
qmd collection remove notes
qmd collection add ~/notes --name notes --mask '**/*.md'
qmd collection add ~ --name home-notes --mask 'todo.md'
Defensive patterns

Strategy: validation

Validate before calling

// Before qmd update: verify every matched file resolves inside the collection root
import { globSync } from 'glob';
import path from 'node:path';

function findEscapes(root: string, mask: string): string[] {
  const absRoot = path.resolve(root);
  return globSync(mask, { cwd: absRoot, follow: true, dot: true })
    .map(f => path.resolve(absRoot, f))
    .filter(f => !f.startsWith(absRoot + path.sep));
}
const escapes = findEscapes('/home/me/notes', '**/*.md');
if (escapes.length) console.warn('Will be skipped as OUTSIDE_COLLECTION:', escapes);

Type guard

function maskStaysInsideRoot(mask: string): boolean {
  return mask.split(',').every(part => {
    const p = part.trim().replace(/^!/, '');
    return !path.isAbsolute(p) && !p.split(/[\\/]/).includes('..');
  });
}

Prevention

When it happens

Trigger: After any indexing run (`qmd collection add`, `qmd update`), the CLI reports each file the store skipped with code OUTSIDE_COLLECTION. The underlying conditions are the same as error 60: escaping glob masks, absolute mask patterns, or file symlinks resolving outside the collection root.

Common situations: Right after adding a collection whose mask accidentally matches files above the root; on `qmd update` after symlinks in the collection were repointed to external targets; after moving the collection directory so old indexed paths now resolve outside; dotfiles-style symlinked note folders.

Related errors


AI-assisted analysis of tobi/qmd@dbfd0b4736 (2026-08-28). Data as JSON: /api/errors/8358d2027f9dd8a5. Report an issue: GitHub.