can1357/oh-my-pi · error · Error
secret placeholder key at ${keyPath} exists but is empty or
Error message
secret placeholder key at ${keyPath} exists but is empty or unreadable What it means
Secret placeholder keys are stored in a key file created with an exclusive 'wx' flag so only one process generates the key. If the file exists but readPlaceholderKeyFile cannot obtain non-empty valid content (another process is mid-write, or the file is corrupted/unreadable), the code refuses to cache or derive a key.
Source
Thrown at packages/coding-agent/src/secrets/index.ts:48
cachedPlaceholderKeys.set(keyPath, existing);
return existing;
}
const generated = crypto.randomBytes(32).toString("base64url");
await fs.promises.mkdir(path.dirname(keyPath), { recursive: true });
try {
await fs.promises.writeFile(keyPath, generated, { flag: "wx", mode: 0o600 });
cachedPlaceholderKeys.set(keyPath, generated);
return generated;
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err;
// Another process won the create race but may still be mid-write: `wx`
// creates the file empty before the bytes land. Wait for non-empty content
// instead of caching an empty key (which would be a known, dictionaryable
// key and would not match tokens other processes persist with the real key).
const winner = await readPlaceholderKeyFile(keyPath, true);
if (winner === undefined) {
throw new Error(`secret placeholder key at ${keyPath} exists but is empty or unreadable`);
}
cachedPlaceholderKeys.set(keyPath, winner);
return winner;
}
}
/** Return an existing placeholder key for redaction without creating a new key file. */
export async function getExistingSecretPlaceholderKey(keyDir?: string): Promise<string | undefined> {
const keyPath = keyDir ? path.join(keyDir, "secret-placeholder.key") : getSecretPlaceholderKeyPath();
const cached = cachedPlaceholderKeys.get(keyPath);
if (cached !== undefined) return cached;
// Redaction-only: this key is loaded solely to redact an existing key file from
// provider-visible tool output, never to mint placeholders. A truncated/corrupt
// or unreadable key must NOT block startup for replace-only/no-secret sessions —
// an invalid key is not a usable HMAC anyway, and a tool reading the same file
// gets the same bytes, so there is nothing sensitive to redact.
let existing: string | undefined;
try {View on GitHub (pinned to 9690622007)
Solutions
- Delete the empty/corrupt key file so a healthy process can recreate it
- Wait and retry — the winning process may still be mid-write
- Check filesystem permissions on the secrets directory
Example fix
// before
// keyPath exists but is 0 bytes
throw new Error(`secret placeholder key at ${keyPath} exists but is empty or unreadable`);
// after
await fs.rm(keyPath); // remove the empty/corrupt file, then retry the operation Defensive patterns
Strategy: retry
Validate before calling
const stat = await fs.stat(keyPath).catch(() => null);
if (stat && stat.size === 0) throw new Error("placeholder key file is empty; delete it and retry"); Type guard
null
Try / catch
try {
key = await getSecretPlaceholderKey();
} catch (err) {
if (err instanceof Error && err.message.includes("empty or unreadable")) {
await Bun.sleep(50); // creator may still be mid-write
key = await getSecretPlaceholderKey();
} else throw err;
} Prevention
- Avoid killing processes during first-run key creation
- Check directory permissions for the secrets path
- On persistent failure, delete the empty key file and let it regenerate
When it happens
Trigger: Concurrent processes racing to create the placeholder key file; the key file exists with zero bytes or cannot be read/decoded.
Common situations: Crash between file creation and write leaving an empty file; permission problems on the secrets directory; heavy multi-process startup (multiple omp instances) hitting the create race.
Related errors
- secret placeholder key at ${keyPath} is invalid
- Unpacked ASAR file '${label}' changed while being read
- Failed to acquire lock for ${filePath} after ${opts.retries}
- unknown filetype: {ft_debug}
- Is a directory
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a521a24a0d69ba52.
Report an issue: GitHub.