can1357/oh-my-pi · error · ToolError

Cannot rewrite external URL: ${rawPath}. Use `read` or `sear

Error message

Cannot rewrite external URL: ${rawPath}. Use `read` or `search` to inspect fetched web content; ast_edit only applies to local files.

What it means

ast_edit only operates on local files. When a target path looks like an external URL (parseReadUrlTarget recognizes it), the resolveExternalUrl hook throws this ToolError directing the caller to `read` or `search` for inspecting fetched web content instead of rewriting it.

Source

Thrown at packages/coding-agent/src/tools/ast-edit.ts:298

					throw new ToolError(`Duplicate rewrite pattern: ${pat}`);
				}
				seenPatterns.add(pat);
			}
			const normalizedRewrites = Object.fromEntries(ops);
			const maxFiles = $envpos("PI_MAX_AST_FILES", 1000);

			const scope = await resolveToolSearchScope({
				rawPaths: params.paths,
				cwd: this.session.cwd,
				internalUrlAction: "rewrite",
				settings: this.session.settings,
				signal,
				sessionFile: this.session.getSessionFile() ?? undefined,
				localProtocolOptions: this.session.localProtocolOptions,
				skills: this.session.skills,
				resolveExternalUrl: async rawPath => {
					if (!parseReadUrlTarget(rawPath)) return undefined;
					throw new ToolError(
						`Cannot rewrite external URL: ${rawPath}. Use \`read\` or \`search\` to inspect fetched web content; ast_edit only applies to local files.`,
					);
				},
			});
			const { searchPath: resolvedSearchPath, scopePath, isDirectory, multiTargets, globFilter } = scope;

			const result = await runAstEditOnce(multiTargets, resolvedSearchPath, globFilter, {
				rewrites: normalizedRewrites,
				dryRun: true,
				maxFiles,
				failOnParseError: false,
				signal,
			});

			const { errors: cappedParseErrors, total: parseErrorsTotal } = capParseErrors(result.parseErrors);
			const formatPath = (filePath: string): string =>
				formatResultPath(filePath, isDirectory, resolvedSearchPath, this.session.cwd);

View on GitHub (pinned to 9690622007)

Solutions

  1. Use a local filesystem path instead of a URL; ast_edit cannot modify remote content.
  2. Fetch the content with the read tool (or download the file), edit the local copy, then re-upload/publish via the appropriate mechanism.
  3. If the URL is actually a local file path misparsed as a URL (e.g. contains '://'), correct the path format.

Example fix

// before
await astEdit.execute({ path: 'https://example.com/src/app.ts', ops });
// after
await astEdit.execute({ path: 'src/app.ts', ops });
Defensive patterns

Strategy: validation

Validate before calling

if (/^[a-z][a-z0-9+.-]*:\/\//i.test(path) || path.startsWith('http')) {
  throw new Error('ast_edit accepts local paths only; fetch remote content first');
}

Type guard

function isLocalPath(target: string): boolean {
  return !/^[a-z][a-z0-9+.-]*:\/\//i.test(target);
}

Try / catch

try {
  await astEditTool.execute({ path, ops }, signal);
} catch (err) {
  if (err instanceof ToolError && err.message.startsWith('Cannot rewrite external URL')) {
    // fall back to read/download the URL, then edit a local copy
  }
}

Prevention

When it happens

Trigger: Calling ast_edit with a path/target like `https://example.com/foo.ts` or another URL scheme that parseReadUrlTarget accepts as a web target.

Common situations: An LLM confusing the read tool's URL-fetch capability with ast_edit's scope; automations piping a previously fetched URL into edit tools; attempts to 'fix' remote code without downloading it first.

Related errors


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