{"record":{"id":"82ddb44264debbad","repo":"can1357/oh-my-pi","slug":"optional-marker-is-only-valid-on-object-proper","errorCode":null,"errorMessage":"optional \"?\" marker is only valid on object property values","messagePattern":"optional \"\\?\" marker is only valid on object property values","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/ir.ts","lineNumber":1500,"sourceCode":"\t\tindex: indexes?.string,\n\t\tsymbolIndex: indexes?.symbol,\n\t\tpatternIndexes: indexes === undefined || indexes.patterns.length === 0 ? undefined : indexes.patterns,\n\t\textras,\n\t};\n\tobject[kSimple] = simple;\n\tobject[kSimpleOwner] = object;\n\treturn object;\n}\n\n/** Parse a definition, optionally resolving names from an enclosing scope. */\nexport function parseDef(def: unknown, resolve?: AliasResolver): IR {\n\tif (typeof def === \"string\") {\n\t\tconst parsed = parseStringDef(def, resolve);\n\t\tif (parsed.hasDefault) {\n\t\t\tthrow new OmpTypeError(\"A default may only be specified for an object property or tuple element\");\n\t\t}\n\t\tif (parsed.optional) {\n\t\t\tthrow new OmpTypeError(`optional \"?\" marker is only valid on object property values`);\n\t\t}\n\t\treturn parsed.ir;\n\t}\n\tif (Array.isArray(def)) {\n\t\tif (def.length === 3 && def[1] === \"=\") {\n\t\t\tthrow new OmpTypeError(\"A default may only be specified for an object property or tuple element\");\n\t\t}\n\t\treturn parseArrayExpression(def, resolve);\n\t}\n\tif (def instanceof RegExp) return patternIR(def);\n\tif (def instanceof Date) return { k: \"lit\", v: def };\n\tif (isEmbedded(def)) return embed(def);\n\tif (typeof def === \"function\") {\n\t\tconst resolved = Reflect.apply(def, undefined, []);\n\t\tif (!isEmbedded(resolved)) {\n\t\t\tthrow new OmpTypeError(`thunk must return a Type (was ${typeof resolved})`);\n\t\t}\n\t\treturn embed(resolved);","sourceCodeStart":1482,"sourceCodeEnd":1518,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/ir.ts#L1482-L1518","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst T = type(\"name?\")\n// after\nconst T = type({ \"name?\": \"string\" })","handlingStrategy":"type-guard","validationCode":"function assertNoTopLevelOptional(def) {\n  if (typeof def === \"string\" && def.endsWith(\"?\"))\n    throw new Error('\"?\" is only valid on object property values');\n}","typeGuard":"const isOptionalMarker = (d) => typeof d === \"string\" && d.endsWith(\"?\");","tryCatchPattern":"try { const T = type(def); } catch (e) {\n  if (e.message.includes('optional \"?\" marker')) {\n    // strip the \"?\" or relocate into an object definition\n  } else throw e;\n}","preventionTips":["Reserve \"?\" for object property value strings","Strip trailing \"?\" when reusing property strings elsewhere","Use unions with undefined for standalone optional semantics"],"tags":["omptype","schema-definition","optional"],"backgroundTag":"invalid-optional-marker","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}