stablyai/orca · error · Error
Managed Claude auth child file is not owned by Orca.
Error message
Managed Claude auth child file is not owned by Orca.
What it means
Thrown by writeClaudeManagedAuthFile() when the target child file (.credentials.json or oauth-account.json) exists but isOwnedChildFile() returns false — i.e. the file's canonical path (realpath) does not lie under the managed auth directory, or it is a symlink, or it is not a regular file. This is a path-traversal / ownership guard: Orca only overwrites files it created inside its own managed account directory.
Source
Thrown at src/main/claude-accounts/managed-auth-path.ts:81
const filePath = resolve(managedAuthPath, filename)
try {
if (!isOwnedChildFile(managedAuthPath, filePath)) {
return null
}
return readFileSync(filePath, 'utf-8')
} catch {
return null
}
}
export function writeClaudeManagedAuthFile(
managedAuthPath: string,
filename: '.credentials.json' | 'oauth-account.json',
contents: string
): void {
const filePath = resolve(managedAuthPath, filename)
if (existsSync(filePath) && !isOwnedChildFile(managedAuthPath, filePath)) {
throw new Error('Managed Claude auth child file is not owned by Orca.')
}
writeFileAtomically(filePath, contents, { mode: 0o600 })
}
function isManagedAuthMarkerValid(markerPath: string, accountId: string): boolean {
try {
if (
!existsSync(markerPath) ||
lstatSync(markerPath).isSymbolicLink() ||
!lstatSync(markerPath).isFile()
) {
return false
}
return readFileSync(markerPath, 'utf-8').trim() === accountId
} catch {
return false
}
}View on GitHub (pinned to 1136503c6a)
Solutions
- Remove the offending file/symlink and let Orca recreate it: delete the child file then re-run the auth write.
- Confirm managedAuthPath resolves under app.getPath('userData')/claude-accounts and was created via resolveOwnedClaudeManagedAuthPath.
- Audit for symlinks in the account auth directory (lstat) and reject/repair before write.
- Re-add the Claude account so Orca recreates a clean owned directory.
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync, lstatSync } from 'node:fs'
function isSafeOwnedChild(parentDir: string, file: string): boolean {
if (!existsSync(file)) return true // new file is fine
const stat = lstatSync(file)
return stat.isFile() && !stat.isSymbolicLink()
} Try / catch
try {
writeClaudeManagedAuthFile(managedAuthPath, '.credentials.json', contents)
} catch (error) {
if (error instanceof Error && /not owned by Orca/.test(error.message)) {
// Recreate the account directory or remove the offending file
throw new Error('Auth file is foreign; re-add the account.')
}
throw error
} Prevention
- Only write into paths resolved by resolveOwnedClaudeManagedAuthPath.
- Never symlink inside the managed auth directory.
- Recreate account directories via the add flow rather than manual file placement.
- Audit for symlinks before write.
When it happens
Trigger: The target file path resolves (via symlink) outside the managedAuthPath. A pre-existing .credentials.json was created by an unrelated process. Someone replaced the file with a symlink. The managed directory was moved/relabeled so realpath no longer matches.
Common situations: User manually placed a .credentials.json in the managed dir. A symlink attack or misconfigured managedAuthPath pointing at a shared location. Stale account directory after a userData migration changed canonical paths.
Related errors
- Upload source escapes selected directory: ${candidatePath}
- Path escaped upload root during staging: '${displayPath}'
- Invalid renderer output path: ${String(outputPath)}
- [verify-packaged-plugin-resources] bundled plugin path escap
- Managed Claude auth storage is not owned by Orca.
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/67952733553aae5a.
Report an issue: GitHub.