can1357/oh-my-pi · error · AIError.ProviderResponseError
GitLab Duo Workflow create response missing workflow id (HTT
Error message
GitLab Duo Workflow create response missing workflow id (HTTP ${response.status}) What it means
Thrown when the create-workflow call returns HTTP ok but the parsed GitLabCreateWorkflowResponse has no id in any known field (id, workflow_id, workflowId). Without a workflow id the client cannot stream events for the workflow, so it fails fast with a ProviderResponseError (kind: empty-body), including the HTTP status for context.
Source
Thrown at packages/ai/src/providers/gitlab-duo-workflow.ts:1730
body: JSON.stringify(body),
signal: gitLabDuoWorkflowRestSignal(signal),
});
traceGitLabDuoWorkflow("workflow.create.response", {
status: response.status,
ok: response.ok,
namespaceId,
hasProjectId: Boolean(projectId),
});
if (!response.ok) {
throw new AIError.GitLabDuoWorkflowApiError(
`GitLab Duo Workflow create failed with HTTP ${response.status}`,
response.status,
);
}
const payload = (await response.json()) as GitLabCreateWorkflowResponse;
const workflowId = payload.id ?? payload.workflow_id ?? payload.workflowId;
if (workflowId === undefined) {
throw new AIError.ProviderResponseError(
`GitLab Duo Workflow create response missing workflow id (HTTP ${response.status})`,
{ provider: "gitlab-duo-agent", kind: "empty-body" },
);
}
traceGitLabDuoWorkflow("workflow.create.id", { workflowId });
return String(workflowId);
}
async function stopGitLabDuoWorkflow(
fetchImpl: FetchImpl,
baseUrl: string,
apiKey: string,
workflowId: string,
): Promise<void> {
// Stop rides a FRESH timeout signal, deliberately decoupled from `options.signal`
// (see the `finally` block in `runGitLabDuoWorkflow`): a run cancelled by the
// caller must still fire the server-side stop, but a stalled PATCH here would
// otherwise leave the `runGitLabDuoWorkflow` promise unresolved forever — theView on GitHub (pinned to 9690622007)
Solutions
- Check the raw create response with curl against your GitLab instance to see where the id lives
- Upgrade GitLab (or pi-ai) so the create response field names match what the client recognizes
- Bypass any proxy/cache that might strip the response body
- Retry — if the Duo Workflow service was mid-restart it may return a complete body next time
Defensive patterns
Strategy: type-guard
Validate before calling
// Confirm the create endpoint returns an id field on your GitLab version:
const probe = await fetch(`${gitlabBaseUrl}/api/v4/version`, { headers: { authorization: `Bearer ${token}` } });
const { version } = await probe.json();
if (compareVersion(version, "16.x") < 0) console.warn("GitLab version may lack workflow id in create response"); Type guard
function hasWorkflowId(p: GitLabCreateWorkflowResponse): p is GitLabCreateWorkflowResponse & { id: string | number } {
return (p.id ?? p.workflow_id ?? p.workflowId) !== undefined;
} Try / catch
try {
const workflowId = await createWorkflow(...);
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.message.includes("missing workflow id")) {
// inspect the raw create response body for the actual id field name / GitLab version drift
} else throw err;
} Prevention
- Match GitLab server version with what the pi-ai client expects for create responses
- Log the raw create JSON on 2xx to catch schema drift early
- Bypass caching proxies that could serve a stripped 2xx body
- Retry once — a restarting Duo Workflow service can return a partial body
When it happens
Trigger: GitLab accepted the create but returned a body without the workflow id — GitLab version whose create response uses an unrecognized field name, a partial/empty 2xx body from a proxy, or Duo Workflow service returning an ack-only body.
Common situations: Self-managed GitLab version mismatch with the client's expected response schema; intermediary caching proxy serving a stripped body; Duo Workflow service degraded while the front door still returns 2xx.
Related errors
- GitLab Duo Workflow direct_access did not return credentials
- GitLab Duo Workflow create failed with HTTP ${response.statu
- Devin API error: response body is empty
- Devin Connect frame length ${len} exceeds ${MAX_CONNECT_FRAM
- Devin auth error: GetUserJwt returned an empty user JWT
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/350a299583ce902a.
Report an issue: GitHub.