{"record":{"id":"2bf1136368cc55e3","repo":"can1357/oh-my-pi","slug":"flickr-getsizes-response-did-not-include-a-direct","errorCode":null,"errorMessage":"flickr getSizes response did not include a direct image URL","messagePattern":"flickr getSizes response did not include a direct image URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-image-hosts.ts","lineNumber":259,"sourceCode":"\tconst status = /<rsp\\b[^>]*\\b(?:stat|status)=[\"']([^\"']+)[\"']/i.exec(xml)?.[1];\n\tif (status && status !== \"ok\") throw new Error(\"flickr rejected the upload\");\n\tconst photoId = /<photoid\\b[^>]*>([^<]+)<\\/photoid>/i.exec(xml)?.[1]?.trim();\n\tif (!photoId) throw new Error(\"flickr response did not include a photo ID\");\n\treturn photoId;\n}\n\nfunction largestFlickrSource(payload: Record<string, unknown>): string {\n\tif (payload.stat !== \"ok\") throw new Error(\"flickr getSizes request failed\");\n\tconst sizes = nestedRecord(payload, \"sizes\", \"flickr\").size;\n\tif (!Array.isArray(sizes)) throw new Error(\"flickr getSizes response did not include sizes\");\n\tfor (let index = sizes.length - 1; index >= 0; index--) {\n\t\tconst size = sizes[index];\n\t\tif (size && typeof size === \"object\" && !Array.isArray(size)) {\n\t\t\tconst source = (size as Record<string, unknown>).source;\n\t\t\tif (typeof source === \"string\" && source) return directUrl(source, \"flickr\");\n\t\t}\n\t}\n\tthrow new Error(\"flickr getSizes response did not include a direct image URL\");\n}\n\nfunction createFlickrUploader(config: DestinationRuntimeConfig): BlobUploader {\n\tconst credentials: FlickrOAuthCredentials = {\n\t\tconsumerKey: requireCredential(config, \"apiKey\"),\n\t\tconsumerSecret: requireCredential(config, \"apiSecret\"),\n\t\ttoken: requireCredential(config, \"oauthToken\"),\n\t\ttokenSecret: requireCredential(config, \"oauthTokenSecret\"),\n\t};\n\tconst uploadFields = flickrUploadFields(config);\n\n\treturn {\n\t\tdestination: \"flickr\",\n\t\tasync upload(request: BlobUploadRequest) {\n\t\t\tconst uploadOAuth = await oauthParameters(\"POST\", FLICKR_UPLOAD_URL, uploadFields, credentials);\n\t\t\tconst uploadResponse = await fetchFor(config)(FLICKR_UPLOAD_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: multipartFile(request, \"photo\", { ...uploadFields, ...uploadOAuth }),","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-image-hosts.ts#L241-L277","documentation":"largestFlickrSource() iterates the sizes array from largest to smallest looking for a 'source' URL string; if none of the entries contain a usable source URL it throws this. The upload cannot produce a direct image link without at least one size URL.","triggerScenarios":"Every entry in the getSizes size array lacks a non-empty string 'source' field — e.g. all sizes are pending generation, the photo is restricted/hidden, or the response schema renamed the field.","commonSituations":"Flickr account with restricted content settings, brand-new photo whose renditions are not ready yet, or Flickr changing the source field name in getSizes output.","solutions":["Retry after a short delay so Flickr finishes generating image renditions","Inspect the getSizes payload to confirm the source field is present on size entries","Check the Flickr account's privacy/restriction settings for the uploaded photo","Verify the Flickr API response format has not changed (source URL field name)"],"exampleFix":"// before\nfor (let index = sizes.length - 1; index >= 0; index--) {\n\tconst size = sizes[index];\n\tif (size && typeof size === \"object\" && !Array.isArray(size)) {\n\t\tconst source = (size as Record<string, unknown>).source;\n\t\tif (typeof source === \"string\" && source) return directUrl(source, \"flickr\");\n\t}\n}\n// after — fall back to the page URL when no direct source exists\nconst fallback = nestedRecord(payload, \"sizes\", \"flickr\").url;\nif (typeof fallback === \"string\" && fallback) return directUrl(fallback, \"flickr\");\nthrow new Error(\"flickr getSizes response did not include a direct image URL\");","handlingStrategy":"fallback","validationCode":"const sizes = payload?.sizes?.size ?? [];\nconst anySource = (Array.isArray(sizes) ? sizes : []).some((s) => typeof s?.source === \"string\" && s.source);\nif (!anySource) console.warn(\"no flickr size has a source URL yet\");","typeGuard":"function hasDirectSource(payload: Record<string, unknown>): boolean {\n\tconst sizes = (payload.sizes as Record<string, unknown> | undefined)?.size;\n\treturn Array.isArray(sizes) && sizes.some((s) => typeof (s as Record<string, unknown>)?.source === \"string\");\n}","tryCatchPattern":"try {\n\treturn largestFlickrSource(payload);\n} catch (err) {\n\tif (err instanceof Error && err.message.includes(\"direct image URL\")) {\n\t\treturn photoPageUrl(photoId); // fallback to the Flickr page URL\n\t}\n\tthrow err;\n}","preventionTips":["Delay URL resolution until renditions are ready (poll getSizes)","Check account content-restriction settings","Fall back to the photo page URL instead of failing the upload","Alert on systematic absence of source fields (schema drift)"],"tags":["flickr","api","response-parsing"],"backgroundTag":"api-response-missing-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}