{"record":{"id":"42968e1d9a53dbed","repo":"linshenkx/prompt-optimizer","slug":"invalid-message-role-msg-role","errorCode":null,"errorMessage":"Invalid message role: ${msg.role}","messagePattern":"Invalid message role: (.+?)","errorType":"validation","errorClass":"RequestConfigError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/llm/adapters/abstract-adapter.ts","lineNumber":193,"sourceCode":"   * @param messages 消息数组\n   * @throws {RequestConfigError} 当消息数组无效时\n   */\n  protected validateMessages(messages: Message[]): void {\n    if (!Array.isArray(messages)) {\n      throw new RequestConfigError('Messages must be an array')\n    }\n\n    if (messages.length === 0) {\n      throw new RequestConfigError('Messages array cannot be empty')\n    }\n\n    for (const msg of messages) {\n      if (!msg.role || !msg.content) {\n        throw new RequestConfigError('Each message must have role and content')\n      }\n\n      if (!['system', 'user', 'assistant', 'tool'].includes(msg.role)) {\n        throw new RequestConfigError(`Invalid message role: ${msg.role}`)\n      }\n\n      if (typeof msg.content !== 'string') {\n        throw new RequestConfigError('Message content must be a string')\n      }\n    }\n  }\n\n  protected validateImageUnderstandingRequest(request: ImageUnderstandingRequest): void {\n    if (!request || typeof request !== 'object') {\n      throw new RequestConfigError('Image understanding request cannot be empty')\n    }\n\n    if (typeof request.userPrompt !== 'string' || !request.userPrompt.trim()) {\n      throw new RequestConfigError('Image understanding user prompt cannot be empty')\n    }\n\n    if (!Array.isArray(request.images) || request.images.length === 0) {","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/llm/adapters/abstract-adapter.ts#L175-L211","documentation":"Thrown by validateMessages when a message's role is not one of 'system' | 'user' | 'assistant' | 'tool'. The adapter only maps these four roles to provider formats, so anything else (e.g. 'function', 'developer', 'tool_call') is rejected client-side with a RequestConfigError that interpolates the offending role.","triggerScenarios":"Passing a message with role 'function', 'developer', 'tool_call', 'Tool' (capitalized), or any custom string; migrating code from another SDK whose role union differs.","commonSituations":"Porting OpenAI SDK v0 code that used role:'function'; typos or casing differences ('User'); custom agent frameworks that invent roles like 'observation'.","solutions":["Remap the role to one of system/user/assistant/tool (e.g. 'function' -> 'tool')","Fix casing and typos in role strings","If you control the schema, constrain role with a union type so mistakes fail at compile time"],"exampleFix":"// before\nmessages = [{ role: 'function', content: '42' }]\n\n// after\nmessages = [{ role: 'tool', content: '42' }]","handlingStrategy":"type-guard","validationCode":"const VALID = new Set(['system','user','assistant','tool'])\nif (!messages.every(m => VALID.has(m.role))) throw new Error('Bad role')","typeGuard":"const VALID_ROLES = new Set(['system','user','assistant','tool'])\nfunction hasValidRole(m: unknown): m is Message {\n  return !!m && typeof m === 'object' && VALID_ROLES.has((m as Message).role)\n}","tryCatchPattern":"try { ... } catch (e) { if (e instanceof RequestConfigError && e.message.startsWith('Invalid message role')) { /* remap role */ } else throw e }","preventionTips":["Use a union type for role: 'system'|'user'|'assistant'|'tool'","Remap legacy roles like 'function' to 'tool' when porting old OpenAI code","Normalize casing before sending"],"tags":["validation","messages","role","llm"],"backgroundTag":"invalid-message-role","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}