Skip to content

Commit add41f9

Browse files
cdervclaude
andcommitted
fix(R): detect and handle x64 R on Windows ARM
Detects x64 R running on Windows ARM and provides helpful error message with ARM64 R download guidance instead of generic installation message. Implementation details: - Parses YAML output to detect architecture mismatch - Uses custom WindowsArmX64RError for type-safe error handling - Detects in both success and failure paths (emulation is intermittent) - Ensures error displays in quarto check output 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent d4cb6a7 commit add41f9

3 files changed

Lines changed: 83 additions & 2 deletions

File tree

src/core/knitr.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import { rBinaryPath, resourcePath } from "./resources.ts";
1111
import { readYamlFromString } from "./yaml.ts";
1212
import { coerce, satisfies } from "semver/mod.ts";
1313
import { debug } from "../deno_ral/log.ts";
14+
import { isWindows } from "../deno_ral/platform.ts";
1415

1516
export interface KnitrCapabilities {
1617
versionMajor: number;
1718
versionMinor: number;
1819
versionPatch: number;
1920
home: string;
2021
libPaths: string[];
22+
platform?: string;
2123
packages: KnitrRequiredPackages;
2224
}
2325

@@ -68,6 +70,29 @@ export async function checkRBinary() {
6870
}
6971
}
7072

73+
export class WindowsArmX64RError extends Error {
74+
constructor(msg: string) {
75+
super(msg);
76+
}
77+
}
78+
79+
function checkWindowsArmR(platform: string | undefined): void {
80+
if (!platform) return;
81+
82+
const isWindowsArm = isWindows && Deno.build.arch === "aarch64";
83+
const isX64R = platform.includes("x86_64") || platform.includes("i386");
84+
85+
if (isWindowsArm && isX64R) {
86+
throw new WindowsArmX64RError(
87+
"x64 R detected on Windows ARM.\n\n" +
88+
"x64 R runs under emulation and is not reliable for Quarto.\n" +
89+
"Please install native ARM64 R. \n" +
90+
"Read about R on 64-bit Windows ARM at https://blog.r-project.org/2024/04/23/r-on-64-bit-arm-windows/\n" +
91+
"After installation, set QUARTO_R environment variable if the correct version is not correctly found.",
92+
);
93+
}
94+
}
95+
7196
export async function knitrCapabilities(rBin: string | undefined) {
7297
if (!rBin) return undefined;
7398
try {
@@ -105,6 +130,8 @@ export async function knitrCapabilities(rBin: string | undefined) {
105130
Object.values(pkgVersRequirement["rmarkdown"]).join(" "),
106131
)
107132
: false;
133+
134+
checkWindowsArmR(caps.platform);
108135
return caps;
109136
} else {
110137
debug("\n++ Problem with results of knitr capabilities check.");
@@ -115,9 +142,35 @@ export async function knitrCapabilities(rBin: string | undefined) {
115142
if (result.stderr) {
116143
debug(` with stderr from R:\n${result.stderr}`);
117144
}
145+
146+
// Check if this is x64 R on Windows ARM
147+
if (result.stdout) {
148+
try {
149+
const yamlMatch = result.stdout.match(
150+
/--- YAML_START ---(.*)--- YAML_END ---/s,
151+
);
152+
if (yamlMatch) {
153+
const yamlLines = yamlMatch[1];
154+
const caps = readYamlFromString(yamlLines) as KnitrCapabilities;
155+
checkWindowsArmR(caps.platform);
156+
}
157+
} catch (e) {
158+
// If it's our specific x64-on-ARM error, rethrow it
159+
if (e instanceof WindowsArmX64RError) {
160+
throw e;
161+
}
162+
// Otherwise YAML parse failed, continue to return undefined
163+
debug(" Failed to parse YAML for architecture detection");
164+
}
165+
}
166+
118167
return undefined;
119168
}
120-
} catch {
169+
} catch (e) {
170+
// Rethrow x64-on-ARM errors - these have helpful messages
171+
if (e instanceof WindowsArmX64RError) {
172+
throw e;
173+
}
121174
debug(
122175
`\n++ Error while running 'capabilities/knitr.R' ${
123176
rBin ? "with " + rBin : ""
@@ -136,6 +189,12 @@ export function knitrCapabilitiesMessage(caps: KnitrCapabilities, indent = "") {
136189
for (const path of caps.libPaths) {
137190
lines.push(` - ${path}`);
138191
}
192+
193+
// Show platform information if available
194+
if (caps.platform) {
195+
lines.push(`Platform: ${caps.platform}`);
196+
}
197+
139198
lines.push(`knitr: ${caps.packages.knitr || "(None)"}`);
140199
if (caps.packages.knitr && !caps.packages.knitrVersOk) {
141200
lines.push(

src/execute/rmd.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
knitrCapabilitiesMessage,
2727
knitrInstallationMessage,
2828
rInstallationMessage,
29+
WindowsArmX64RError,
2930
} from "../core/knitr.ts";
3031
import {
3132
DependenciesOptions,
@@ -139,13 +140,23 @@ title: "Title"
139140
const kMessage = "Checking R installation...........";
140141
let caps: KnitrCapabilities | undefined;
141142
let rBin: string | undefined;
143+
let x64ArmError: WindowsArmX64RError | undefined;
142144
const json: Record<string, unknown> = {};
143145
if (conf.jsonResult) {
144146
(conf.jsonResult.tools as Record<string, unknown>).knitr = json;
145147
}
146148
const knitrCb = async () => {
147149
rBin = await checkRBinary();
148-
caps = await knitrCapabilities(rBin);
150+
try {
151+
caps = await knitrCapabilities(rBin);
152+
} catch (e) {
153+
if (e instanceof WindowsArmX64RError) {
154+
x64ArmError = e;
155+
// Don't rethrow - let caps stay undefined but capture error
156+
} else {
157+
throw e; // Rethrow other errors
158+
}
159+
}
149160
};
150161
if (conf.jsonResult) {
151162
await knitrCb();
@@ -205,6 +216,16 @@ title: "Title"
205216
checkInfoMsg(msg);
206217
json["installed"] = false;
207218
checkInfoMsg("");
219+
} else if (x64ArmError) {
220+
// x64 R on Windows ARM detected - show specific error
221+
json["installed"] = false;
222+
checkCompleteMessage(kMessage + "(None)\n");
223+
const errorLines = x64ArmError.message.split("\n");
224+
errorLines.forEach((line) => {
225+
checkInfoMsg(line);
226+
});
227+
json["error"] = x64ArmError.message;
228+
checkInfoMsg("");
208229
} else if (caps === undefined) {
209230
json["installed"] = false;
210231
checkCompleteMessage(kMessage + "(None)\n");

src/resources/capabilities/knitr.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ cat("libPaths:\n")
88
for (lib in .libPaths()) {
99
cat(paste0(' - ', shQuote(lib)), "\n")
1010
}
11+
cat("platform:", R.version[['platform']], "\n")
1112
cat("packages:\n")
1213
cat(" knitr: ")
1314
if (requireNamespace("knitr", quietly = TRUE)) {

0 commit comments

Comments
 (0)