gitroomhq/postiz-app · error · BadBody

Tumblr did not return a valid post response

Error message

Tumblr did not return a valid post response

What it means

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.

Source

Thrown at libraries/nestjs-libraries/src/integrations/social/tumblr.provider.ts:550

        });
      }

      const { data: response } = await this.getSsrfSafeAxios().post(
        `${TUMBLR_API_URL}/blog/${encodeURIComponent(blogName)}/posts`,
        formData,
        {
          headers: {
            ...formData.getHeaders(),
            Authorization: `Bearer ${accessToken}`,
            'User-Agent': TUMBLR_USER_AGENT,
          },
        }
      );

      // axios parses JSON silently: a non-JSON 200 (proxy/WAF page) comes
      // back as a raw string, which would publish the post with an empty id.
      if (!response || typeof response !== 'object' || !response.response) {
        throw new BadBody(
          this.identifier,
          String(response || '{}'),
          '{}',
          'Tumblr did not return a valid post response'
        );
      }

      return response as TumblrCreatePostResponse;
    }, this.identifier);
  }

  private async createContentBlocks(post: PostDetails<TumblrDto>) {
    const content: TumblrContentBlock[] = [];

    if (post.settings?.title) {
      content.push({
        type: 'text',
        subtype: 'heading1',

View on GitHub (pinned to 0f1647f749)

Solutions

  1. Log the raw response string attached to the BadBody to see what actually came back
  2. Retry after a short wait — WAF/rate-limit pages are often transient
  3. Bypass proxies/VPNs for api.tumblr.com, or add it to proxy allowlists
  4. If the envelope genuinely changed (no .response field), update the parser to the current Tumblr API shape
Defensive patterns

Strategy: retry

Validate before calling

if (typeof parsed === 'string' || parsed?.startsWith('<')) { /* non-JSON response: back off and retry later */ }

Type guard

const isTumblrPostResponse = (r: unknown): r is { response: { id: string } } => typeof r === 'object' && r !== null && 'response' in (r as object);

Try / catch

catch (e) { if (e instanceof BadBody && e.message.includes('valid post response')) { /* retry with backoff; likely WAF/proxy page */ } throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: Self-hosted Postiz behind a corporate proxy; Tumblr API returning an unexpected envelope after an API version change; network middleboxes tampering with responses.

Related errors


AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27). Data as JSON: /api/errors/c50a68746a5ecccc. Report an issue: GitHub.