You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
.First() and .First.sys() are bypassed during the startup sequence #158
# Session Information
arf version: 0.3.0
OS: windows (x86_64)
Config file: ~\AppData\Roaming\arf\arf.toml
R version: 4.5.3
R_HOME: ~\.R/versions/4.5.3
R source: PATH
rig: not installed
air: not installed
Current mode: R
R history: ~\AppData\Roaming\arf\history\r.db
Shell history: ~\AppData\Roaming\arf\history\shell.db
## Environment Variables
R_LIBS_USER: ~\.R/library/4.5
R_LIBS_SITE: ~\.R/versions/4.5.3/site-library
Description
Description:
I've been testing arf as a modern drop-in replacement for radian and standard R, but I've run into an issue with how the frontend handles the legacy R startup sequence.
Specifically, arf evaluates the user .Rprofile correctly, but it appears to skip executing .First() and .First.sys() at the end of the initialization pipeline.
Impact:
Because these standard hooks never fire, it breaks two major workflows:
VS Code Compatibility (vscode-R): The VS Code session watcher relies on overwriting .First.sys() to delay its attachment until the base packages load. Because arf skips this hook, the IDE's Workspace, Plot viewer, and Data viewer permanently fail to attach to the arf session.
User Profiles: Any user-defined startup logic or message() calls wrapped in .First() inside a .Rprofile are completely swallowed.
Steps to reproduce
Place the following in an ~/.Rprofile:
.First<-function() {
message("--> This message should print at startup.")
}
start arf
Expected behavior
--> This message should print at startup. is displayed before banner or prompt.
Actual behavior
Nothing is displayed. If .First() is run in terminal, message is displayed.
Thank you for looking into this! I think we're half way there. .First() seems to be evaluated properly now, but .First.sys() is still not being run because R is still not attaching the environment. When .First.sys() is run manually, everything attaches seemingly just fine. Would you like me to raise another issue or reopen this one?
arf follows the startup sequence described in ?Startup:
Next, if a function .First is found on the search path, it is executed as .First(). Finally, function .First.sys() in the base package is run.
Concretely:
.First() is looked up starting from R_GlobalEnv (i.e. the search path)
.First.sys() is looked up in R_BaseNamespace and evaluated in R_BaseEnv — i.e. the function "in the base package"
This mirrors what R's own setup_Rmainloop() does (src/main/main.c, around the .First.sys block): R_findVar(cmd, baseNSenv) followed by eval(lang1(cmd), R_BaseEnv).
It places .First.sys in the global environment, not in the base package. ?Startup explicitly describes .First.sys() as "the function in the base package", so a user-level override in globalenv isn't part of the documented contract — it happens to work in plain R because of implementation behavior in setup_Rmainloop() that we haven't fully traced yet (the documented lookup path shouldn't pick up a globalenv binding, yet empirically it does in a stock R session).
Why arf behaves differently
arf runs setup_Rmainloop() with load_init_file = FALSE on Windows (needed for globalCallingHandlers() compatibility) and sources .Rprofile manually afterwards. The manual invocation of .First.sys() is then done via the C API with the same documented lookup (R_BaseNamespace / R_BaseEnv), which — as documented — resolves to base::.First.sys. It does not pick up the globalenv binding that vscode-R installs.
As I noted, Ark is likely to have the same problem for the same reason, since Ark also disables the built-in profile loading and sources .Rprofile manually.
Documented alternatives for vscode-R
Any of the following would not depend on implementation details of setup_Rmainloop():
Put the init logic in .First() (the documented user hook) instead of .First.sys().
Use setHook(packageEvent("base", "onLoad"), ...) / setHook("rstudio.sessionInit", ...) style hooks.
Install the wrapper into the base namespace directly:
@eitsupi, thank you again for such a quick fix and a through explanation! I agree with your plan and I'll head over to vscode-R to suggest a more elegant solution on their end. For now, I've placed this statement inside my .First() call.
if (
Sys.getenv("TERM_PROGRAM") =="vscode"&&
exists(".First.sys", envir=.GlobalEnv)
) {
.GlobalEnv$.First.sys()
if (exists(".First.sys", envir=.GlobalEnv, inherits=FALSE)) {
rm(".First.sys", envir=.GlobalEnv)
}
}
Just so I know I'm understanding correctly, both ark and arf are now handling .First() and .First.sys() the same way, right?
Activity
eitsupi commentedon Apr 22, 2026
Thanks for pointing that out.
Could you try #159?
bsirak commentedon Apr 22, 2026
Thank you for looking into this! I think we're half way there.
.First()seems to be evaluated properly now, but.First.sys()is still not being run because R is still not attaching the environment. When.First.sys()is run manually, everything attaches seemingly just fine. Would you like me to raise another issue or reopen this one?eitsupi commentedon Apr 22, 2026
@bsirak Thank you. Do you have any examples that can be reproduced?
eitsupi commentedon Apr 22, 2026
I've investigated, but it doesn't seem like an easy fix.
I suspect the same problem might occur in Ark as well.
When does this actually occur?
If it only happens in vscode-r, it might be faster to rewrite it in vscode-r.
See REditorSupport/vscode-R#1684
eitsupi commentedon Apr 22, 2026
To clarify the technical situation:
What arf does
arf follows the startup sequence described in
?Startup:Concretely:
.First()is looked up starting fromR_GlobalEnv(i.e. the search path).First.sys()is looked up inR_BaseNamespaceand evaluated inR_BaseEnv— i.e. the function "in thebasepackage"This mirrors what R's own
setup_Rmainloop()does (src/main/main.c, around the.First.sysblock):R_findVar(cmd, baseNSenv)followed byeval(lang1(cmd), R_BaseEnv).What vscode-R does
vscode-R's
R/session/init.Rdoes:It places
.First.sysin the global environment, not in the base package.?Startupexplicitly describes.First.sys()as "the function in thebasepackage", so a user-level override in globalenv isn't part of the documented contract — it happens to work in plain R because of implementation behavior insetup_Rmainloop()that we haven't fully traced yet (the documented lookup path shouldn't pick up a globalenv binding, yet empirically it does in a stock R session).Why arf behaves differently
arf runs
setup_Rmainloop()withload_init_file = FALSEon Windows (needed forglobalCallingHandlers()compatibility) and sources.Rprofilemanually afterwards. The manual invocation of.First.sys()is then done via the C API with the same documented lookup (R_BaseNamespace/R_BaseEnv), which — as documented — resolves tobase::.First.sys. It does not pick up the globalenv binding that vscode-R installs.As I noted, Ark is likely to have the same problem for the same reason, since Ark also disables the built-in profile loading and sources
.Rprofilemanually.Documented alternatives for vscode-R
Any of the following would not depend on implementation details of
setup_Rmainloop():.First()(the documented user hook) instead of.First.sys().setHook(packageEvent("base", "onLoad"), ...)/setHook("rstudio.sessionInit", ...)style hooks.?Startup.)Given that arf is spec-conformant here, I think REditorSupport/vscode-R#1684 is the right place to address this.
bsirak commentedon Apr 22, 2026
@eitsupi, thank you again for such a quick fix and a through explanation! I agree with your plan and I'll head over to
vscode-Rto suggest a more elegant solution on their end. For now, I've placed this statement inside my.First()call.Just so I know I'm understanding correctly, both
arkandarfare now handling.First()and.First.sys()the same way, right?eitsupi commentedon Apr 25, 2026
@bsirak I don't know because I haven't actually tried it myself.