{"record":{"id":"709993206fe4693f","repo":"tinyhumansai/openhuman","slug":"no-screenshots-found-in-screenshotdir","errorCode":null,"errorMessage":"No screenshots found in ${screenshotDir}","messagePattern":"No screenshots found in (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/ios-appstore-metadata.mjs","lineNumber":303,"sourceCode":"    data: {\n      type: \"appScreenshots\",\n      id: reservation.data.id,\n      attributes: {\n        uploaded: true,\n        sourceFileChecksum: checksum,\n      },\n    },\n  });\n  console.log(`[ios-appstore-metadata] uploaded ${fileName}`);\n}\n\nasync function uploadScreenshots(localization) {\n  const files = (await readdir(screenshotDir))\n    .filter((file) => file.endsWith(\".png\"))\n    .sort()\n    .map((file) => path.join(screenshotDir, file));\n  if (!files.length) {\n    throw new Error(`No screenshots found in ${screenshotDir}`);\n  }\n\n  await deleteExistingScreenshotSet(localization);\n  const set = await createScreenshotSet(localization);\n  for (const file of files) {\n    await uploadScreenshot(set, file);\n  }\n}\n\nawait updateAppInfoLocalization();\nconst versionLocalization = await updateVersionLocalization();\nawait uploadScreenshots(versionLocalization);\n\nconsole.log(\n  \"[ios-appstore-metadata] metadata and screenshots submitted to App Store Connect.\",\n);\n","sourceCodeStart":285,"sourceCodeEnd":320,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/ios-appstore-metadata.mjs#L285-L320","documentation":"uploadScreenshots() globs fastlane/screenshots/en-US for files ending exactly in '.png' (case-sensitive), sorts them, and refuses to run when the list is empty. This check deliberately runs BEFORE deleteExistingScreenshotSet(), so an empty directory fails safely instead of wiping the live screenshot set and uploading nothing.","triggerScenarios":"Fresh clone where screenshots were never captured; screenshots exist under a different locale directory (the path is hardcoded to fastlane/screenshots/en-US and does NOT follow ASC_LOCALE); files named .PNG/.jpg (endsWith('.png') is case-sensitive); wrong working directory assumptions.","commonSituations":"Running the metadata script before the screenshot-capture step on a new machine; a capture tool writing `01_home.PNG` uppercase extensions; screenshots landing in fastlane/screenshots/de-DE while the script only reads en-US.","solutions":["Capture or copy the PNGs into <repo>/fastlane/screenshots/en-US before running","Rename files to lowercase .png (e.g. `for f in *.PNG; do mv \"$f\" \"${f%.PNG}.png\"; done`)","Re-run `node scripts/ios-appstore-metadata.mjs`; existing remote screenshots are only deleted once local files are confirmed present"],"exampleFix":"# before\nls fastlane/screenshots/en-US   # empty → script aborts\n\n# after\nmkdir -p fastlane/screenshots/en-US\ncp ~/captures/*.png fastlane/screenshots/en-US/\nnode scripts/ios-appstore-metadata.mjs","handlingStrategy":"validation","validationCode":"import { readdir } from 'node:fs/promises';\nconst pngs = (await readdir('fastlane/screenshots/en-US')).filter(f => f.endsWith('.png'));\nif (pngs.length === 0) {\n  console.error('No lowercase-.png screenshots in fastlane/screenshots/en-US — capture them first');\n  process.exit(1);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run the capture step (WDIO/fastlane snapshot) before the metadata script in the same pipeline and fail the pipeline if it produced zero PNGs","Normalize screenshot filenames to lowercase .png — the glob is case-sensitive","Keep the check-before-delete ordering: the empty-dir abort is what protects the live App Store screenshot set from being wiped"],"tags":["app-store-connect","screenshots","file-system","release"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}