can1357/oh-my-pi · error · ToolError

Cannot find internal URL without a backing file: ${rawPatter

Error message

Cannot find internal URL without a backing file: ${rawPattern}

What it means

Non-glob internal URL path entries are resolved to their backing file path via InternalUrlRouter.resolve with pathOnly: true. If the resolved resource has no sourcePath, there is no local file to substitute into the search, so the tool throws with the offending rawPattern in the message.

Source

Thrown at packages/coding-agent/src/tools/glob.ts:250

					if (!resource.sourcePath) {
						throw new ToolError(`Cannot find internal URL without a backing file: ${memoryGlob.baseUrl}`);
					}
					normalizedPatterns.push(
						path.join(resource.sourcePath.replace(/[*?[{]/g, "[$&]"), memoryGlob.globPattern),
					);
					continue;
				}
				const resource = await internalRouter.resolve(rawPattern, {
					cwd: this.session.cwd,
					settings: this.session.settings,
					signal,
					sessionFile: this.session.getSessionFile() ?? undefined,
					localProtocolOptions: this.session.localProtocolOptions,
					skills: this.session.skills,
					pathOnly: true,
				});
				if (!resource.sourcePath) {
					throw new ToolError(`Cannot find internal URL without a backing file: ${rawPattern}`);
				}
				normalizedPatterns.push(resource.sourcePath);
			}
			if (normalizedPatterns.some(pattern => pattern.length === 0)) {
				throw new ToolError("`path` must contain non-empty globs or paths");
			}

			// Tolerate missing entries in a multi-path call: skip ones whose base
			// directory is gone, and only error if every entry is missing. Single
			// missing path keeps the original ENOENT semantics — the user explicitly
			// asked about that one path, so silent empty results would be misleading.
			let missingPaths: string[] = [];
			let effectivePatterns = normalizedPatterns;
			if (normalizedPatterns.length > 1 && !this.#customOps) {
				const partition = await partitionExistingPaths(normalizedPatterns, this.session.cwd, parseFindPattern);
				if (partition.valid.length === 0) {
					throw new ToolError(`Path not found: ${partition.missing.join(", ")}`);
				}

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the internal URL points to a file-backed resource and the session has the right localProtocolOptions/skills
  2. Use the direct path or read tool for virtual resources that cannot be globbed
  3. Fix the URL so InternalUrlRouter.resolve returns a resource with sourcePath

Example fix

// before
await findTool({ path: "skill://virtual-resource" }); // no backing file
// after
await findTool({ path: "skill://real-skill/README.md" });
Defensive patterns

Strategy: type-guard

Validate before calling

const resource = await internalRouter.resolve(url, { cwd, settings, signal, pathOnly: true });
if (!resource.sourcePath) throw new Error(`no backing file for ${url}`);

Type guard

const hasBackingFile = (r: { sourcePath?: string | null }): r is { sourcePath: string } =>
  typeof r.sourcePath === "string" && r.sourcePath.length > 0;

Try / catch

try {
  await globTool({ path: internalUrl });
} catch (err) {
  if (err instanceof ToolError && /without a backing file/.test(err.message)) {
    // use read on the virtual resource or correct the URL
  } else throw err;
}

Prevention

When it happens

Trigger: Passing a bare internal URL (e.g. 'skill://foo' without glob chars) that resolves to a resource without a sourcePath — scheme targets without an on-disk representation.

Common situations: Referencing generated/virtual resources that exist only in memory; typo'd internal URLs; using a scheme whose handler doesn't map to files in the current session (missing localProtocolOptions/skills).

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/5dfa36a6aa20c0f7. Report an issue: GitHub.