can1357/oh-my-pi · error · OmpTypeError
optional key ${String(key)} cannot specify a default
Error message
optional key ${String(key)} cannot specify a default What it means
A property marked optional with the "?" marker cannot also declare a default value — the two are mutually exclusive in omptype's IR (optional means the key may be absent; a default means it is always materialized). parseObjectDefinition throws this when a parsed property has both opt and hasDefault set.
Source
Thrown at packages/omptype/src/ir.ts:1431
prop = {
key,
opt,
val: isObjectDefinition(val) ? parseObjectDefinition(val, resolve) : parseDef(val, resolve),
};
}
if (key !== originalKey) {
if (!spreadKeys?.has(key) && props.some(candidate => candidate.key === key)) {
throw new OmpTypeError(`duplicate object key ${String(key)}`);
}
if (normalizedKey === undefined) normalizedKey = key;
else {
normalizedKeys ??= [normalizedKey];
normalizedKeys.push(key);
}
} else if (!spreadKeys?.has(key) && (key === normalizedKey || normalizedKeys?.includes(key))) {
throw new OmpTypeError(`duplicate object key ${String(key)}`);
}
if (opt && prop.hasDefault) throw new OmpTypeError(`optional key ${String(key)} cannot specify a default`);
if (simple && (prop.hasDefault || !isSimpleIR(prop.val))) simple = false;
addObjectProp(props, spreadKeys, prop);
}
for (const key of Object.getOwnPropertySymbols(def)) {
if (!Object.prototype.propertyIsEnumerable.call(def, key)) continue;
const val = def[key];
let prop: PropIR;
if (typeof val === "string") {
const parsed = parseStringDef(val, resolve);
prop = { key, opt: parsed.optional, val: parsed.ir };
if (parsed.hasDefault) {
prop.def = parsed.def;
prop.hasDefault = true;
}
} else if (Array.isArray(val) && val.length === 2 && val[1] === "?") {
prop = { key, opt: true, val: parseDef(val[0], resolve) };
} else if (Array.isArray(val) && val.length === 3 && val[1] === "=") {
prop = {View on GitHub (pinned to 9690622007)
Solutions
- Drop the "?" marker and keep the default (the default already makes the key optional in effect)
- Keep "?" and remove the default
- If the field should always exist with a fallback, use the default-only form
Example fix
// before
type({ "port?": ["=", "8080"] })
// after
type({ port: ["=", "8080"] }) // default implies optionality Defensive patterns
Strategy: validation
Validate before calling
function assertOptionalWithoutDefault(key, val) {
const isOptional = key.endsWith("?") || key.endsWith("?:");
const hasDefault = (Array.isArray(val) && val.length === 3 && val[1] === "=") || val?.hasDefault === true;
if (isOptional && hasDefault) throw new Error(`optional key ${key} cannot also have a default`);
} Try / catch
try { const T = type(def); } catch (e) {
if (String(e.message).startsWith("optional key")) {
// drop either the "?" marker or the default, then retry
} else throw e;
} Prevention
- Treat a default as implying optionality; never add "?" to defaulted properties
- When generating defs from TS types, skip emitting defaults for optional members
- Document the "?"-xor-default rule in schema-authoring guidelines
When it happens
Trigger: Defining a property like "id?:" with a defaulted value form ([key, "=", value] or an embedded type with hasDefault) in parseObjectDefinition, i.e. opt === true while prop.hasDefault is true.
Common situations: Adding "?" to a property that previously had a default; auto-generating schemas from TS types where optional fields also carry defaults; misunderstanding that "?" plus default are redundant in ArkType-like DSLs.
Related errors
- A default may only be specified for an object property or tu
- optional "?" marker is only valid on object property values
- unsupported default literal in "${this.#src}"
- invalid index signature pattern ${keyDefinition}
- duplicate object key ${String(key)}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1393b78db7dafc9e.
Report an issue: GitHub.