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
- Use a local filesystem path instead of a URL; ast_edit cannot modify remote content.
- Fetch the content with the read tool (or download the file), edit the local copy, then re-upload/publish via the appropriate mechanism.
- 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
- Resolve URLs to local files before any edit-tool call.
- Route remote content through the read/search tools, not edit tools.
- Validate target paths look like filesystem paths in tool-calling pipelines.
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
- Provider delete URL must not embed an account credential
- ${destination} returned an invalid upload URL
- Destination option endpoint must be an absolute URL
- Destination option endpoint must use HTTP or HTTPS
- Destination option ${optionName} must be an absolute HTTP UR
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/078d7056b8a093e4.
Report an issue: GitHub.