can1357/oh-my-pi · error · Error

secret placeholder key at

Error message

secret placeholder key at ${keyPath} is invalid

What it means

The persisted placeholder key file must contain a valid key value. If the file is readable but its contents fail validation (invalidValue is defined — wrong format, corrupted bytes), the loader throws instead of using an untrusted value that would not match tokens encrypted by other processes.

Solutions

  1. Delete the invalid key file and let the library regenerate it (note: previously persisted placeholder tokens will need re-derivation)
  2. Restore the file from a valid backup
  3. Avoid manually modifying files under the secrets directory

Example fix

// before
// keyPath contains malformed bytes
throw new Error(`secret placeholder key at ${keyPath} is invalid`);
// after
await fs.rm(keyPath); // regenerate a valid key
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
  key = await getExistingSecretPlaceholderKey(keyPath);
} catch (err) {
  if (err instanceof Error && err.message.includes("is invalid")) {
    await fs.rm(keyPath); // regenerate
    key = await getSecretPlaceholderKey();
  } else throw err;
}

Prevention

When it happens

Trigger: Reading an existing placeholder key whose file content does not decode to a valid key; manual editing or truncation of the key file; disk corruption.

Common situations: Hand-editing files in the secrets directory; syncing secrets across machines with incompatible formats; partial writes from a crash.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8eca95f131f7dacb. Report an issue: GitHub.

Appendix: source

Thrown at packages/coding-agent/src/secrets/index.ts:145

}

/** Read and validate the key file, optionally retrying briefly until a valid key lands. */
async function readPlaceholderKeyFile(keyPath: string, retry: boolean): Promise<string | undefined> {
	const attempts = retry ? 50 : 1;
	let invalidValue: string | undefined;
	for (let attempt = 0; attempt < attempts; attempt++) {
		if (attempt > 0) await Bun.sleep(10);
		try {
			const value = (await Bun.file(keyPath).text()).trim();
			if (PLACEHOLDER_KEY_RE.test(value)) return value;
			if (value.length > 0) invalidValue = value;
		} catch (err) {
			if (isEnoent(err)) return undefined;
			throw err;
		}
	}
	if (invalidValue !== undefined) {
		throw new Error(`secret placeholder key at ${keyPath} is invalid`);
	}
	return undefined;
}

type RawSecretEntry = Omit<SecretEntry, "friendlyName"> & { friendlyName?: unknown };

export {
	deobfuscateSessionContext,
	deobfuscateToolArguments,
	obfuscateMessages,
	obfuscateProviderContext,
} from "./message-transform";
export { type SecretEntry, SecretObfuscator } from "./obfuscator";
export { secretEntriesNeedPlaceholderKey, secretEntryNeedsPlaceholderKey } from "./placeholder";

/**
 * Load secrets from project-local and global secrets.yml files.
 * Project-local entries override global entries with matching content.

View on GitHub (pinned to 9690622007)