can1357/oh-my-pi · error
Provider delete URL must not embed an account credential
Error message
Provider delete URL must not embed an account credential
What it means
sanitizeDeleteAction scrubss a provider's remote delete action so the persisted handle never embeds the account credential. If the delete URL (origin, path, or hash — and even an unparseable raw string) contains the literal credential substring, this error is thrown instead of storing it. Query parameters that embed the credential are silently stripped; only origin/path/hash fragments are hard-rejected.
Source
Thrown at packages/coding-agent/src/blob-broker/provider-file-types.ts:183
}
return normalized;
}
function errorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
function containsCredential(value: string, credential: string): boolean {
return credential.length > 0 && value.includes(credential);
}
function sanitizeDeleteAction(action: RemoteDeleteAction, credential: string): RemoteDeleteAction {
let url: URL;
try {
url = new URL(action.url);
} catch {
if (containsCredential(action.url, credential)) {
throw new Error("Provider delete URL must not embed an account credential");
}
url = new URL(action.url, "https://provider-file.invalid");
}
if (containsCredential(url.origin + url.pathname + url.hash, credential)) {
throw new Error("Provider delete URL must not embed an account credential");
}
for (const name of [...url.searchParams.keys()]) {
const values = url.searchParams.getAll(name);
if (
SENSITIVE_QUERY_PARAMETERS[name.toLowerCase()] ||
values.some(value => containsCredential(value, credential))
) {
url.searchParams.delete(name);
}
}
let headers: Record<string, string> | undefined;
if (action.headers) {
for (const name in action.headers) {View on GitHub (pinned to 9690622007)
Solutions
- Change the provider adapter to pass credentials via headers or query parameters, never in origin/path/hash.
- Use a token-redacting URL builder so the credential never appears in the URL string.
- If the credential is a broad substring colliding innocently, ensure the credential value used for hashing/matching is the actual secret, not a derived or shared string.
- Rotate the credential if it was ever persisted.
Example fix
// before
{ url: `https://api.p.com/v1/files/${id}/token=${credential}` }
// after
{ url: `https://api.p.com/v1/files/${id}`, headers: { Authorization: `Bearer ${credential}` } } Defensive patterns
Strategy: validation
Validate before calling
function urlEmbedsCredential(rawUrl: string, credential: string): boolean {
if (!credential) return false;
try {
const u = new URL(rawUrl);
return (u.origin + u.pathname + u.hash).includes(credential);
} catch {
return rawUrl.includes(credential);
}
}
// call before constructing the delete action; move the credential to headers if true Try / catch
try {
const handle = persistProviderFileHandle(rawHandle, credential);
} catch (err) {
if (err instanceof Error && err.message.includes("must not embed an account credential")) {
// rebuild delete action with Authorization header instead of URL credential
return persistProviderFileHandle({ ...rawHandle, delete: withHeaderAuth(rawHandle.delete, credential) }, credential);
}
throw err;
} Prevention
- Never put API tokens in the URL origin, path, or fragment — use headers
- Pass credentials via query parameters only if unavoidable (they get stripped, not rejected)
- Review custom provider adapters for URL interpolation of secrets
- Rotate any credential that may have been persisted in a handle
When it happens
Trigger: A provider adapter builds RemoteDeleteAction.url with the API key/token in the URL — e.g. "https://api.example.com/files/f1?key=<token>" where the token also appears in the path, or a relative URL whose raw text contains the credential while also failing URL parsing.
Common situations: A custom/less common provider puts auth in the URL path or fragment; an adapter interpolates the token into the pathname by mistake; the credential accidentally equals a generic substring appearing in the URL (over-broad match).
Related errors
- ${destination} returned an invalid upload URL
- ${destination} returned an unsupported upload URL
- Destination option endpoint must be an absolute URL
- Destination option endpoint must use HTTP or HTTPS
- Destination paths cannot contain parent traversal or NUL byt
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/bf6210068a358dfe.
Report an issue: GitHub.