moeru-ai/airi · error
Security-scoped Extension import access is not configured.
Error message
Security-scoped Extension import access is not configured.
What it means
withSecurityScopedAccess wraps an import operation in a macOS security-scoped bookmark session so the main process can read a folder the user picked in the sandboxed renderer. It throws when a bookmark exists but the injected startAccessingSecurityScopedResource hook is missing. This means the Electron main side was not wired with the browser-window security-scoped accessors, so the service cannot begin the scoped read it promised.
Solutions
- Wire the Electron main-process security-scoped accessors (dialog/bookmark based startAccessingSecurityScopedResource) into the service constructor/DI container where DirectoryImportService is created.
- Ensure a securityScopedBookmark is only stored on platforms that provide the accessor; clear the bookmark if the helper is unavailable so operation() runs without scoped access.
- Reconstruct the service with the full dependency object (check apps/stage-tamagotchi/src/main services wiring for the missing field).
- In tests, inject a no-op startAccessingSecurityScopedResource that returns a stop function.
Example fix
// before
new DirectoryImportService({ extensionsRoot, isExtensionInstalled })
// after
new DirectoryImportService({
extensionsRoot,
isExtensionInstalled,
startAccessingSecurityScopedResource: bookmark => {
const stop = airiStartAccessingSecurityScopedResource(bookmark)
return () => stop()
},
}) Defensive patterns
Strategy: try-catch
Validate before calling
if (!serviceHasSecurityScopedAccess) {
// fall back to a non-bookmarked import path or reject before prepare
} Type guard
function isSecurityScopedAccessConfigured(s: { startAccessingSecurityScopedResource?: (b: string) => () => void }): s is typeof s & { startAccessingSecurityScopedResource: (b: string) => () => void } {
return typeof s.startAccessingSecurityScopedResource === 'function'
} Try / catch
try {
await importService.prepare(folderPath, bookmark)
} catch (error) {
if (error.message.includes('Security-scoped Extension import access is not configured')) {
// wire the Electron main helper or retry without a bookmark
}
} Prevention
- Always construct the service with the full Electron main-process dependency set via injeca.
- Only persist securityScopedBookmark values on platforms where the accessor exists.
- Inject a no-op accessor in tests so bookmarked paths are exercisable.
When it happens
Trigger: Calling prepare/commitPreparedPlan on an extension import whose stored plan carries a securityScopedBookmark while the DirectoryImportService was constructed without startAccessingSecurityScopedResource (null/undefined dependency injection of the Electron main-process helper).
Common situations: Running on Linux/Windows where the bookmark helper is intentionally not wired but a bookmark value was still persisted from another platform; constructing the service in tests or secondary windows without the injeca-provided Electron helpers; refactors that dropped the startAccessingSecurityScopedResource argument.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- checkMacOSScreenCapturePermission is only available on…
- No screen source available
- requestMacOSScreenCapturePermission is only available on…
- Apple Speech transcription requires the Electron desktop…
- Computer use has stopped.
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17).
Data as JSON: /api/errors/9a40f5b8a1c642df.
Report an issue: GitHub.
Appendix: source
Thrown at apps/stage-tamagotchi/src/main/services/airi/plugins/host/directory-import.ts:458
await this.initialization
await this.commitQueue
}
private assertActive(): void {
if (this.disposed) {
throw new Error('Extension directory importer is disposed.')
}
}
private async withSecurityScopedAccess<TResult>(
bookmark: string | undefined,
operation: () => Promise<TResult>,
): Promise<TResult> {
if (!bookmark) {
return await operation()
}
if (!this.startAccessingSecurityScopedResource) {
throw new Error('Security-scoped Extension import access is not configured.')
}
const stopAccessing = this.startAccessingSecurityScopedResource(bookmark)
try {
return await operation()
}
finally {
stopAccessing()
}
}
private async assertDestinationAvailable(extensionId: string): Promise<void> {
const destination = join(this.extensionsRoot, extensionId)
if (await this.isExtensionInstalled(extensionId) || await pathExists(destination)) {
throw new Error(`Extension is already installed: ${extensionId}`)
}
}
View on GitHub (pinned to 438a067dde)