{"record":{"id":"3219ef89d51b45c0","repo":"linshenkx/prompt-optimizer","slug":"label-index-1-must-not-provide-both-asset","errorCode":null,"errorMessage":"${label} #${index + 1} must not provide both assetId and b64.","messagePattern":"(.+?) #(.+?) must not provide both assetId and b64\\.","errorType":"validation","errorClass":"EvaluationValidationError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/evaluation/service.ts","lineNumber":1685,"sourceCode":"\n  private validateMediaItems(mediaItems: EvaluationMediaItem[], label: string): void {\n    mediaItems.forEach((item, index) => {\n      const itemLabel = item?.label?.trim() || '';\n      const assetId = item?.assetId?.trim() || '';\n      const b64 = item?.b64?.trim() || '';\n\n      if (!itemLabel) {\n        throw new EvaluationValidationError(`${label} #${index + 1} label must not be empty.`);\n      }\n\n      if (!assetId && !b64) {\n        throw new EvaluationValidationError(\n          `${label} #${index + 1} must provide either assetId or b64.`\n        );\n      }\n\n      if (assetId && b64) {\n        throw new EvaluationValidationError(\n          `${label} #${index + 1} must not provide both assetId and b64.`\n        );\n      }\n    });\n  }\n\n  private validateContentBlock(\n    block: EvaluationContentBlock | undefined,\n    label: string\n  ): void {\n    if (!block) {\n      throw new EvaluationValidationError(`${label} must not be empty.`);\n    }\n    if (!block.label?.trim()) {\n      throw new EvaluationValidationError(`${label} label must not be empty.`);\n    }\n    const hasContent = !!block.content?.trim();\n    const hasMedia = this.hasBlockMedia(block);","sourceCodeStart":1667,"sourceCodeEnd":1703,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/evaluation/service.ts#L1667-L1703","documentation":"The mirror of the previous rule: a media item must not supply both assetId and b64, because the service cannot decide which source of truth to use for the image. Validation throws when both fields are non-empty on the same item.","triggerScenarios":"A media item whose assetId and b64 are both non-empty strings. Typically introduced when code sets b64 as a default and then also assigns an assetId (or vice versa).","commonSituations":"Migrating from b64 to assetId-based flow without clearing the old field; spread-merging objects that accidentally combine both; form UI that populates both inputs.","solutions":["Keep exactly one of assetId or b64 on the media item and delete the other","Prefer assetId for large/stored images and b64 only for small inline payloads","Strip the unused field in a pre-submit sanitizer"],"exampleFix":"// before\nmedia: [{ label: 'img', assetId: 'img-1', b64: base64String }]\n\n// after\nmedia: [{ label: 'img', assetId: 'img-1' }]","handlingStrategy":"validation","validationCode":"function pickOneSource(item: { label: string; assetId?: string; b64?: string }) {\n  return item.assetId?.trim()\n    ? { label: item.label, assetId: item.assetId }\n    : { label: item.label, b64: item.b64 };\n}\nconst sanitized = media.map(pickOneSource);","typeGuard":"null","tryCatchPattern":"try {\n  await evaluationService.create(request);\n} catch (err) {\n  if (err instanceof EvaluationValidationError && /must not provide both/.test(err.message)) {\n    request = stripConflictingFields(request); // keep assetId, drop b64\n  } else throw err;\n}","preventionTips":["Strip b64 whenever you set assetId (and vice versa) at construction time","Use one code path per source mechanism instead of merging objects","Sanitize payloads before submit"],"tags":["evaluation","validation","media","conflicting-fields"],"backgroundTag":"schema-validation-failed","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}