{"record":{"id":"1d16028cfdb62156","repo":"can1357/oh-my-pi","slug":"invalid-wait-regex-error-instanceof-error-err","errorCode":null,"errorMessage":"Invalid wait regex: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Invalid wait regex: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/broker.ts","lineNumber":1071,"sourceCode":"\t\t\ttimedOut,\n\t\t\tstate: record.snapshot.state,\n\t\t};\n\t}\n\n\tasync #wait(operation: Extract<DaemonOperation, { op: \"wait\" }>): Promise<DaemonRpcResult> {\n\t\tconst record = this.#record(operation.name);\n\t\t// A wait observes exactly one launch generation. Automatic or explicit\n\t\t// relaunches reuse the managed record, so polling the record without this\n\t\t// binding can hang past an exit or consume the replacement's output.\n\t\tconst boundGeneration = record.generation;\n\t\tawait this.#refreshDetached(record);\n\t\tlet matched: string | undefined;\n\t\tlet pattern: RegExp | undefined;\n\t\tif (operation.pattern) {\n\t\t\ttry {\n\t\t\t\tpattern = new RegExp(operation.pattern, \"u\");\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Invalid wait regex: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t}\n\t\t}\n\t\t// Readiness was actually observed: the sticky readyAt survives a fast\n\t\t// ready→exit, a live \"ready\" state, or a \"running\" daemon with no ready spec.\n\t\tconst readyObserved = (): boolean =>\n\t\t\trecord.snapshot.readyAt !== undefined ||\n\t\t\trecord.snapshot.state === \"ready\" ||\n\t\t\t(record.snapshot.state === \"running\" && !record.spec.ready);\n\t\tconst generationEnded = (): boolean =>\n\t\t\trecord.generation !== boundGeneration || record.snapshot.state === \"restarting\";\n\t\tconst condition = (): boolean => {\n\t\t\tif (generationEnded()) return true;\n\t\t\tif (pattern) {\n\t\t\t\tconst match = pattern.exec(record.readinessBuffer);\n\t\t\t\tif (!match) return false;\n\t\t\t\tmatched = match[0].slice(0, 500);\n\t\t\t\treturn true;\n\t\t\t}","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/broker.ts#L1053-L1089","documentation":"DaemonBroker's wait/send operation handler compiles operation.pattern with new RegExp(pattern, \"u\") to match daemon log output during a wait. An invalid pattern aborts the wait before any matching is attempted, wrapping the RegExp syntax error in this message. Like the readiness check, it enforces unicode-mode validity.","triggerScenarios":"Calling a wait operation ({ op: \"wait\", pattern: \"...\" }) with a syntactically invalid regex or one illegal under the 'u' flag (dangling quantifiers, unbalanced groups, bad \\u escapes).","commonSituations":"Patterns built dynamically by string concatenation that end up malformed; unescaped user input embedded into a pattern; JSON config backslash escaping mistakes.","solutions":["Validate the pattern client-side with `new RegExp(pattern, \"u\")` before issuing the wait operation.","Escape user-provided fragments with a regex-escape helper before embedding them in the pattern.","Omit pattern to wait purely on state/readiness instead of log matching."],"exampleFix":"// before\nawait broker.wait({ name: \"server\", pattern: \"ready (\" + userPort + \")\" });\n// after\nconst safe = userPort.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\nconst pattern = `ready \\\\(${safe}\\\\)`;\nnew RegExp(pattern, \"u\"); // validate early\nawait broker.wait({ name: \"server\", pattern });","handlingStrategy":"validation","validationCode":"if (op.pattern !== undefined) {\n  try { new RegExp(op.pattern, \"u\"); }\n  catch (e) { throw new Error(`Bad wait pattern: ${e.message}`); }\n}","typeGuard":"function isValidRegexU(p: unknown): p is string {\n  if (typeof p !== \"string\") return false;\n  try { new RegExp(p, \"u\"); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  await broker.wait({ name, pattern, timeoutMs });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid wait regex:\")) {\n    // correct or drop the pattern, then retry\n  } else throw err;\n}","preventionTips":["Regex-escape any dynamic/user-supplied fragments before composing the pattern.","Compile the pattern once at construction time so invalid patterns fail fast, not at wait time.","Prefer waiting on state/ready flags over log regexes when possible."],"tags":["regex","validation","wait"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}