Skip to content

FIX: Inngest Running Jobs page (/jobs) returning 404 - #212

Merged
mbeps merged 1 commit into
mainfrom
dev
Nov 9, 2026
Merged

mbeps merged 1 commit into
mainfrom
dev

Conversation

@mbeps

@mbeps mbeps commented Nov 9, 2026

Copy link
Copy Markdown
Owner

1. High-Level Overview

Goal: Fix the Inngest API failure on /jobs using the modern Inngest REST v2 pipeline (/v2/runs) exclusively (no legacy fallbacks), pin the Inngest container image in docker-compose.yml to a fixed version (v1.45.1) instead of latest, normalize base URL resolution, flag offline: true on HTTP errors, and eliminate repeated background polling toast spam.

flowchart TD
    subgraph UI ["Client Dashboard (JobsView)"]
        Mount["Mount / Poll Cycle"] -->|"listJobs()"| Action["Server Action: listJobs()"]
        Action -->|"Result<ListJobsResult>"| HandleResult["Process Result"]
        HandleResult -->|"offline === true"| ShowBanner["Render JobsOfflineBanner (No Toast)"]
        HandleResult -->|"isManualRefresh && error && !offline"| ShowToast["toast.error()"]
    end

    subgraph Service ["Inngest Run Service (Pure REST v2)"]
        Action --> CleanBase["resolveInngestApiBaseUrl()\n(Strip trailing slash & /api/inngest)"]
        CleanBase --> RestV2["buildRunsUrl()\n(GET /v2/runs?query=event.data.userId==...)"]
        RestV2 -->|"HTTP 200 OK"| ParseRuns["Normalize Inngest Runs (JobItem[])"]
        RestV2 -->|"HTTP Error (4xx/5xx) or Connection Drop"| MarkOffline["Return { jobs: [], offline: true, error }"]
    end

    subgraph Infra ["Local Container (docker-compose.yml)"]
        ImagePin["Pin image: inngest/inngest:v1.45.1"] --> RunningContainer["Predictable Inngest Dev Server"]
    end
Loading

Global Constraints

  • Pure Modern REST v2: No GraphQL (/v0/gql) or legacy endpoints. Single unified pipeline across dev server and cloud.
  • Pinned Container Version: Replace latest with explicit tag inngest/inngest:v1.45.1 in docker-compose.yml matching other services (postgres:17, qdrant/qdrant:v1.20.0).
  • Zero-Git modifications: Never commit, stage, or push via git. Only read-only git operations allowed.
  • Tenant Isolation: Mandatory multi-tenant isolation via CEL query event.data.userId == "${userId}" and in-memory payload verification.
  • YAGNI / Ponytail Principle: Radical simplicity. Minimum code that works, zero legacy baggage.

2. Alternatives Evaluated

Approach Pros Cons Verdict
Approach 1: Pure REST v2 Fix + Pinned Image v1.45.1 (Recommended) Predictable, reproducible container environment; no surprise breaking changes from latest; removes breaking include=output query param; handles URL misconfiguration cleanly; no legacy GraphQL baggage. None. Selected. Clean, robust, matches user instructions precisely.
Approach 2: Keep latest with REST v2 Fix Automatic image updates. latest can change unexpectedly and break API contract at any time without developer control. Rejected. User explicitly requested pinning version.
Approach 3: Legacy GraphQL fallback Supports outdated CLI versions. Adds dead code and deprecated GraphQL query contracts. Rejected. User rejected legacy fallbacks.

3. Root Cause Analysis

  1. Incompatible Query Parameter (include=output):
    In lib/inngest/run-service.ts, buildRunsUrl sets url.searchParams.set("include", "output"). Inngest Dev Server /v2/runs REST API rejects this parameter with HTTP 400: {"errors":[{"code":"invalid_field_format","message":"unsupported include value \"output\""}]}. When omitted, /v2/runs returns HTTP 200 OK.
  2. Base URL Normalization:
    If INNGEST_BASE_URL contains /api/inngest (e.g. from developer confusing the serve endpoint with the engine endpoint) or a trailing slash, appending /v2/runs produces an invalid route (e.g. .../api/inngest/v2/runs) which returns HTTP 404 from Next.js.
  3. Missing offline: true on HTTP Errors:
    When res.ok is false in fetchUserInngestRuns, it returns { jobs: [], error: ... } with offline: undefined. Because offline is not true, the dashboard cannot show the JobsOfflineBanner, and instead triggers raw error toasts.
  4. Duplicate Toasts on Mount and Polling:
    React 19 mount and useJobsPolling visibility listeners trigger loadJobs immediately and every 5 seconds. Without gating by isManualRefresh, each failed poll emits toast.error.
  5. Unpinned Inngest Docker Image Tag:
    docker-compose.yml currently uses inngest/inngest:latest, which prevents deterministic control over the engine version.

@mbeps
mbeps merged commit a2504cd into main Nov 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant