can1357/oh-my-pi · error · ToolError

Cannot ${internalUrlAction} internal URL without a backing f

Error message

Cannot ${internalUrlAction} internal URL without a backing file: ${rawPath}

What it means

After the InternalUrlRouter resolves an internal URL in path-only mode, the resource must expose a `sourcePath` (a backing local file) for search tools to operate on. Resources whose content is generated in memory or held outside the filesystem cannot back a search scope, so this ToolError is thrown naming the action and URL.

Source

Thrown at packages/coding-agent/src/tools/path-utils.ts:1567

			);
		}
		if (hasGlobPathChars(rawPath)) {
			throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
		}
		const resource = await internalRouter.resolve(rawPath, {
			cwd,
			settings: opts.settings,
			signal: opts.signal,
			sessionFile: opts.sessionFile,
			localProtocolOptions: opts.localProtocolOptions,
			skills: opts.skills,
			// Tool-scope resolution only needs `sourcePath`; skip content
			// materialization so large artifacts (or any handler that separates
			// path from content) stay searchable without OOM risk.
			pathOnly: true,
		});
		if (!resource.sourcePath) {
			throw new ToolError(`Cannot ${internalUrlAction} internal URL without a backing file: ${rawPath}`);
		}
		if (opts.trackImmutableSources && resource.immutable) {
			immutableSourcePaths.add(path.resolve(resource.sourcePath));
		}
		resolvedPathInputs.push(resource.sourcePath);
	}

	let missingPaths: string[] = [];
	let effectivePaths = resolvedPathInputs;
	if (resolvedPathInputs.length > 1) {
		const partition = await partitionExistingPaths(resolvedPathInputs, cwd, parseSearchPath);
		if (partition.valid.length === 0) {
			throw new ToolError(`Path not found: ${partition.missing.join(", ")}`);
		}
		effectivePaths = partition.valid;
		missingPaths = partition.missing;
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Use `read` on the internal URL to fetch its content and search the text directly instead of a file-backed scope search.
  2. Target a resource variant that has a backing file (e.g. the persisted transcript rather than a computed view).
  3. If you own the handler, materialize the resource to a file so sourcePath is populated for path-only resolution.

Example fix

// before
search({ pattern: "panic", paths: ["agent://run-42/summary"] }) // no backing file
// after
const res = await read({ path: "agent://run-42/summary" });
searchInText(res.content, "panic");
Defensive patterns

Strategy: fallback

Validate before calling

const resource = await internalRouter.resolve(p, { cwd, pathOnly: true });
if (!resource.sourcePath) {
  // use read + in-memory search instead of scope search
}

Type guard

const hasBackingFile = (resource) => typeof resource?.sourcePath === "string" && resource.sourcePath.length > 0;

Try / catch

try { scope = await resolveToolSearchScope(opts); }
catch (e) {
  if (e.message.includes("without a backing file")) {
    const res = await read({ path: p });
    return searchInText(res.content, pattern);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling search/ast_grep/ast_edit with an internal URL (e.g. some session://, agent://, or history:// resources) whose handler returns a resource without sourcePath when resolved with `pathOnly: true` in resolveToolSearchScope.

Common situations: Searching a virtual resource that only renders content on demand (e.g. a computed summary, an in-memory artifact) rather than one persisted to a file; handler updated to separate path from content so older consumers now hit the missing sourcePath.

Related errors


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