can1357/oh-my-pi · error · OmpTypeError
optional "?" marker is only valid on object property values
Error message
optional "?" marker is only valid on object property values
What it means
The "?" optional marker is only meaningful on object property values, where it marks the key as possibly absent. If parseStringDef returns parsed.optional for a definition parsed at top level (not as an object property value), parseDef rejects it because there is no enclosing key to make optional.
Source
Thrown at packages/omptype/src/ir.ts:1500
index: indexes?.string,
symbolIndex: indexes?.symbol,
patternIndexes: indexes === undefined || indexes.patterns.length === 0 ? undefined : indexes.patterns,
extras,
};
object[kSimple] = simple;
object[kSimpleOwner] = object;
return object;
}
/** Parse a definition, optionally resolving names from an enclosing scope. */
export function parseDef(def: unknown, resolve?: AliasResolver): IR {
if (typeof def === "string") {
const parsed = parseStringDef(def, resolve);
if (parsed.hasDefault) {
throw new OmpTypeError("A default may only be specified for an object property or tuple element");
}
if (parsed.optional) {
throw new OmpTypeError(`optional "?" marker is only valid on object property values`);
}
return parsed.ir;
}
if (Array.isArray(def)) {
if (def.length === 3 && def[1] === "=") {
throw new OmpTypeError("A default may only be specified for an object property or tuple element");
}
return parseArrayExpression(def, resolve);
}
if (def instanceof RegExp) return patternIR(def);
if (def instanceof Date) return { k: "lit", v: def };
if (isEmbedded(def)) return embed(def);
if (typeof def === "function") {
const resolved = Reflect.apply(def, undefined, []);
if (!isEmbedded(resolved)) {
throw new OmpTypeError(`thunk must return a Type (was ${typeof resolved})`);
}
return embed(resolved);View on GitHub (pinned to 9690622007)
Solutions
- Remove the "?" marker from the standalone definition
- Move the definition back into an object property position where "?" is valid
- Express the optionality at the consumer (e.g. a union with undefined) if it must be standalone
Example fix
// before
const T = type("name?")
// after
const T = type({ "name?": "string" }) Defensive patterns
Strategy: type-guard
Validate before calling
function assertNoTopLevelOptional(def) {
if (typeof def === "string" && def.endsWith("?"))
throw new Error('"?" is only valid on object property values');
} Type guard
const isOptionalMarker = (d) => typeof d === "string" && d.endsWith("?"); Try / catch
try { const T = type(def); } catch (e) {
if (e.message.includes('optional "?" marker')) {
// strip the "?" or relocate into an object definition
} else throw e;
} Prevention
- Reserve "?" for object property value strings
- Strip trailing "?" when reusing property strings elsewhere
- Use unions with undefined for standalone optional semantics
When it happens
Trigger: Calling parseDef/type() with a standalone string such as "name?" or an array expression element containing the optional marker — any def that reaches the typeof def === "string" branch of parseDef with parsed.optional true.
Common situations: Copy-pasting a property string like "age?: number-ish" out of an object definition into a standalone type; building alias bodies or tuple items with "?" markers; template strings that leaked the "?" suffix.
Related errors
- optional key ${String(key)} cannot specify a default
- invalid index signature pattern ${keyDefinition}
- duplicate object key ${String(key)}
- A default may only be specified for an object property or tu
- thunk must return a Type (was ${typeof resolved})
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/82ddb44264debbad.
Report an issue: GitHub.