{"record":{"id":"a1e6afba175949ba","repo":"tonhowtf/omniget","slug":"post-not-found-via-gql","errorCode":null,"errorMessage":"Post not found via GQL","messagePattern":"Post not found via GQL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/instagram.rs","lineNumber":438,"sourceCode":"            .await?;\n\n        if !response.status().is_success() {\n            return Err(anyhow!(\"Instagram GQL retornou HTTP {}\", response.status()));\n        }\n\n        let json: serde_json::Value = response.json().await?;\n\n        let data = json\n            .get(\"data\")\n            .ok_or_else(|| anyhow!(\"Resposta GQL sem data\"))?;\n\n        let media = data\n            .get(\"xdt_shortcode_media\")\n            .or_else(|| data.get(\"shortcode_media\"));\n\n        match media {\n            Some(m) if !m.is_null() => Ok(m.clone()),\n            _ => Err(anyhow!(\"Post not found via GQL\")),\n        }\n    }\n\n    async fn request_embed(&self, post_id: &str) -> anyhow::Result<serde_json::Value> {\n        let url = format!(\"https://www.instagram.com/p/{}/embed/captioned/\", post_id);\n\n        let response = self\n            .client\n            .get(&url)\n            .header(\n                \"Accept\",\n                \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n            )\n            .header(\"Accept-Language\", \"en-GB,en;q=0.9\")\n            .header(\"Sec-Fetch-Dest\", \"iframe\")\n            .header(\"Sec-Fetch-Mode\", \"navigate\")\n            .header(\"Sec-Fetch-Site\", \"cross-site\")\n            .header(\"Referer\", \"https://www.instagram.com/\")","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/instagram.rs#L420-L456","documentation":"request_gql posts to Instagram's graphql/query endpoint (PolarisPostActionLoadPostQueryQuery) and looks for the media payload under data.xdt_shortcode_media (or the legacy data.shortcode_media). This error is thrown when the HTTP response succeeded but neither key exists or the value is JSON null — i.e. Instagram accepted the query but returned no media node for the requested shortcode. It usually means Instagram refused to serve the post anonymously (login wall, deleted/private post) or the GQL doc-id/headers are stale.","triggerScenarios":"Calling get_media_info on an Instagram post/reel whose GQL response has data but data.xdt_shortcode_media and data.shortcode_media are absent or null; non-existent or deleted shortcode; private account; Instagram rate-limiting or login-walling the anonymous query.","commonSituations":"Instagram changed the GraphQL schema or doc_id so the key was renamed; the anonymous cookie/app-id became invalid; post is from a private account; post was deleted between URL validation and fetch; heavy scraping caused a soft-block returning an empty data object.","solutions":["Verify the post is public and exists by opening it in a browser/incognito before retrying.","Check that GQL_DOC_ID, x-ig-app-id and the anonymous cookie flow are up to date; update them if Instagram rotated the endpoint.","Log the raw JSON response at the failure point to see whether Instagram returned an error message or challenge instead of data.","Rely on the existing fallback chain (request_embed then fallback_ytdlp) — ensure the ytdlp fallback is available and up to date.","Add retries with backoff for transient rate-limit responses rather than treating every failure as permanent."],"exampleFix":"// before\nlet media = data.get(\"xdt_shortcode_media\").or_else(|| data.get(\"shortcode_media\"));\n// after\nlet media = data\n    .get(\"xdt_shortcode_media\")\n    .or_else(|| data.get(\"shortcode_media\"))\n    .filter(|m| !m.is_null())\n    .with_context(|| format!(\"GQL returned no media for post; keys={:?}\", data.as_object().map(|o| o.keys().collect::<Vec<_>>())))?;","handlingStrategy":"fallback","validationCode":"// Rust: check the URL looks like a canonical post before calling\nfn looks_like_post(url: &str) -> bool {\n    url.contains(\"/p/\") || url.contains(\"/reel/\") || url.contains(\"/tv/\")\n}","typeGuard":"fn has_media_node(data: &serde_json::Value) -> bool {\n    data.get(\"data\")\n        .and_then(|d| d.get(\"xdt_shortcode_media\").or_else(|| d.get(\"shortcode_media\")))\n        .map(|m| !m.is_null())\n        .unwrap_or(false)\n}","tryCatchPattern":"match downloader.get_media_info(url).await {\n    Ok(info) => use(info),\n    Err(e) if e.to_string().contains(\"Post not found via GQL\") => {\n        // post is private/deleted or GQL schema changed; use embed or yt-dlp fallback\n        fallback_extract(url).await\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Only pass canonical public post/reel URLs","Keep GQL doc_id and app-id headers updated when Instagram rotates them","Always wire the embed/yt-dlp fallback path","Log raw GQL responses to detect schema changes early"],"tags":["instagram","graphql","scraping","network"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}