{"record":{"id":"c50a68746a5ecccc","repo":"gitroomhq/postiz-app","slug":"tumblr-did-not-return-a-valid-post-response","errorCode":null,"errorMessage":"Tumblr did not return a valid post response","messagePattern":"Tumblr did not return a valid post response","errorType":"exception","errorClass":"BadBody","httpStatus":null,"severity":"error","filePath":"libraries/nestjs-libraries/src/integrations/social/tumblr.provider.ts","lineNumber":550,"sourceCode":"        });\n      }\n\n      const { data: response } = await this.getSsrfSafeAxios().post(\n        `${TUMBLR_API_URL}/blog/${encodeURIComponent(blogName)}/posts`,\n        formData,\n        {\n          headers: {\n            ...formData.getHeaders(),\n            Authorization: `Bearer ${accessToken}`,\n            'User-Agent': TUMBLR_USER_AGENT,\n          },\n        }\n      );\n\n      // axios parses JSON silently: a non-JSON 200 (proxy/WAF page) comes\n      // back as a raw string, which would publish the post with an empty id.\n      if (!response || typeof response !== 'object' || !response.response) {\n        throw new BadBody(\n          this.identifier,\n          String(response || '{}'),\n          '{}',\n          'Tumblr did not return a valid post response'\n        );\n      }\n\n      return response as TumblrCreatePostResponse;\n    }, this.identifier);\n  }\n\n  private async createContentBlocks(post: PostDetails<TumblrDto>) {\n    const content: TumblrContentBlock[] = [];\n\n    if (post.settings?.title) {\n      content.push({\n        type: 'text',\n        subtype: 'heading1',","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/gitroomhq/postiz-app/blob/0f1647f7491a217d43eb5ae7a480484bdf0aff3e/libraries/nestjs-libraries/src/integrations/social/tumblr.provider.ts#L532-L568","documentation":"createMultipartPost expected an object response with a .response field from Tumblr's multipart post API, but axios returned a non-object (usually a string). This happens when a proxy/WAF/HTML page answers with 200 but non-JSON, which would otherwise publish with an empty post id.","triggerScenarios":"A 200 response whose body is HTML (Cloudflare interstitial, captive portal, rate-limit page) so axios returns a raw string instead of parsed JSON; or the API shape changed so response.response is missing.","commonSituations":"Self-hosted Postiz behind a corporate proxy; Tumblr API returning an unexpected envelope after an API version change; network middleboxes tampering with responses.","solutions":["Log the raw response string attached to the BadBody to see what actually came back","Retry after a short wait — WAF/rate-limit pages are often transient","Bypass proxies/VPNs for api.tumblr.com, or add it to proxy allowlists","If the envelope genuinely changed (no .response field), update the parser to the current Tumblr API shape"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if (typeof parsed === 'string' || parsed?.startsWith('<')) { /* non-JSON response: back off and retry later */ }","typeGuard":"const isTumblrPostResponse = (r: unknown): r is { response: { id: string } } => typeof r === 'object' && r !== null && 'response' in (r as object);","tryCatchPattern":"catch (e) { if (e instanceof BadBody && e.message.includes('valid post response')) { /* retry with backoff; likely WAF/proxy page */ } throw e; }","preventionTips":["Retry on non-JSON 200s — they are usually transient proxy/WAF pages","Keep api.tumblr.com out of transform proxies that mangle bodies","Alert when a non-JSON body is received so silent envelope changes are caught"],"tags":["tumblr","multipart-upload","non-json-response","waf"],"backgroundTag":"unexpected-response-format","analyzedSha":"0f1647f7491a217d43eb5ae7a480484bdf0aff3e","analyzedAt":"2026-08-27T12:09:55.020Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}