Errors

Failed API calls return an HTTP status code. Structured capability errors return their Hydracept error information in a detail object; some standard HTTP errors use a string or validation details in detail.

A job that was successfully admitted and later failed is different: GET /v1/jobs/{jobId} returns the job resource with terminal status failed and error: {code, message}. Do not confuse an HTTP request error with the error recorded on a durable job.

Typical statuses

StatusMeaning
400Invalid request / policy rejection
401Missing or invalid Hydracept API key / session
402Funding required, budget prevent-mode block, or managed wallet insufficient
403Your API key, project, or environment is not allowed to make this request
404Capability, job, or resource not found
410Resource gone (expired session or retired endpoint)
409Conflict (including idempotency conflicts)
422Request validation failed
429Rate limited
503Provider unavailable

Structured capability errors

When an endpoint returns a structured capability error, its body has this shape:


{
  "detail": {
    "code": "BudgetExceeded",
    "message": "Budget exceeded for principal:usr_example (estimated $0.0500, $0.0000 of $5.0000 remaining this UTC day)",
    "details": {
      "scope": "principal:usr_example",
      "enforcement": "prevent",
      "estimatedCost": 0.05,
      "available": 0.0,
      "ceiling": 5.0
    }
  }
}

BudgetExceeded is returned only when a project or caller has opted into prevent mode. Default is no enforcement: spend ceilings are accounting, not a refuse gate. Opt in with budget.enforcement / executionConstraints.budgetEnforcement (warn or prevent) plus ceilings, or project budget_policy_json.enforcement.

ModeAdmission
off (default)Call proceeds. No 402.
warnCall proceeds. Result/job includes warnings with scope, remaining, and ceiling.
preventHTTP 402 BudgetExceeded with the scope and remaining USD.

Setting maxCostUsd / maximumEstimatedCost on a request without an explicit mode is caller opt-in to prevent for that call.

detail.code is the stable Hydracept error code. detail.message is a human-readable explanation. Validation failures may use a different detail shape.

Unknown or conflicting execution fields

Hydracept never acknowledges an execution request while silently discarding part of the caller’s execution intent. Unknown fields on the invoke/job envelope, execution, executionConstraints, or sampling return 422 instead of being ignored. Capability input is still validated by that capability’s schema.


{
  "detail": {
    "code": "UNKNOWN_EXECUTION_FIELD",
    "path": "execution.executionConstraints.modelPin",
    "message": "Unknown execution field 'modelPin'. Did you mean 'preferredModel'?",
    "didYouMean": "preferredModel",
    "supported": [
      "preferredModel",
      "providerPin",
      "autoSelect",
      "maxCostUsd",
      "budgetEnforcement"
    ],
    "retryable": false
  }
}

Sending the same execution constraint both at the envelope root (preferredModel) and under execution.executionConstraints with different values returns CONFLICTING_EXECUTION_CONSTRAINT. Hydracept does not pick a winner.

Prompt overflow

Semantic prompt fields that exceed inputConstraints return 422 with PromptTooLong. Hydracept does not trim the prompt and continue.


{
  "detail": {
    "code": "PromptTooLong",
    "message": "Prompt is 947 characters; model.generate.3d.v1 accepts at most 800.",
    "field": "input.prompt",
    "actualCharacters": 947,
    "maxCharacters": 800,
    "retryable": false
  }
}

Discover the limit from GET /v1/capabilities/{key}inputConstraints before submit. See Capabilities.

Handle the HTTP status first. When detail.code is present, use that Hydracept value rather than provider-native error strings.

Admission failures from invoke/jobs may return {code, message, details} at the top level (not wrapped in detail). MCP tools surface code, connectUrl / upgradeUrl, and nextAction when present.

CodeWhat to do
USE_JOBSPOST /v1/capabilities/{key}/jobs (submit in the error body)
USE_INVOKEPOST /v1/capabilities/{key}/invoke
CONNECTION_REQUIREDOpen details.connectUrl (or connectUrl) so a human connects BYOK
EXTERNAL_ACTIONS_DISABLEDOpen details.upgradeUrl (Studio billing)
QUOTE_MISMATCHOmit execution.quoteId and submit with a new idempotencyKey
billing_managed_usage_exhaustedOpen upgradeUrl or connect BYOK

Managed inference

CodeMeaning
funding_requiredManaged first-use allowance used, ineligible, or per-job trial ceiling hit. details.fundingOptions is managed and/or byok. The capability is still ready; additional execution needs funding or a provider connection.
billing_managed_usage_exhaustedLegacy alias for the same funding boundary (details.legacyCode). Prefer funding_required. Wallet balance too low for the authorized estimate.
billing_not_configuredStripe / billing not enabled for this environment

Quotes and capability discovery do not consume managed first-use capacity. When trial capacity is exhausted, Hydracept does not silently charge an unrelated payment method.

When billingModes is omitted, the capability is not inference-priced (domain, DNS, and CPU processing). When billingModes.managed.available is false, use BYOK or choose another capability — do not rely on submit errors for discovery.

Terminal job errors

A durable job may be admitted successfully and fail later. Read the job itself:


GET /v1/jobs/{jobId}

{
  "jobId": "wfr_...",
  "status": "failed",
  "error": {
    "code": "PROVIDER_ERROR",
    "message": "The provider could not complete the job"
  },
  "nextAction": "inspect_error"
}

For automation, branch on error.code; error.message is explanatory text. Do not parse provider-native strings to infer a stable class.

If the user says “the last job failed” and the ID is no longer in context, do not ask them to retrieve it manually. Use the prompt-free project history first:


hydracept_jobs_find(intent="failed")
→ hydracept_job_inspect(job_id)

or GET /v1/projects/{projectId}/jobs?outcome=failed. The list exposes only errorCode, never the full error message or request prompt. hydracept_job_inspect intentionally reads one job and bundles its error, diagnostics, request snapshot, and receipt summary.

A retry is a new execution attempt: copy the inspected request input, remove the old idempotencyKey, and submit with a new one. On QUOTE_MISMATCH, also omit stale execution.quoteId / estimateId.

Idempotency

Durable job submits accept idempotencyKey (minted if omitted). Replaying the same key with the same payload returns the existing jobId for queued, running, succeeded, and failed jobs — a client poll timeout is not a Hydracept failure. 409 IdempotencyConflict is only for a different normalized payload on that key; those bodies include jobId. Follow the job's nextAction / retry.newKeySafe before minting a new key. TRANSPORT_AMBIGUOUS is never new-key-safe.

409 with detail.code QUOTE_MISMATCH means a previously sealed quoteId no longer matches the job body. Omit execution.quoteId and submit with a new idempotencyKey.