You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AgentAction: "Tell the user the URL is wrong or the resource no longer exists. Have them check the path against https://instanode.dev/docs — anon resources also auto-expire after 24h, so re-provision if needed.",
346
+
// BUG-API-105 (QA 2026-05-29): the agent_action used to tack on
347
+
// "anon resources also auto-expire after 24h" — irrelevant when
348
+
// the 404 is a typo'd internal path or unknown route, and
349
+
// actively misleading on authenticated misroutes. Hint kept
350
+
// behind a "see /docs" pointer; the auto-expire footnote now
351
+
// only fires when the surface-specific 404 (resource_not_found
352
+
// / webhook_not_found / etc.) emits it.
353
+
AgentAction: "Tell the user the URL is wrong or the resource no longer exists. Have them check the path against https://instanode.dev/docs — if they were calling a token-keyed URL the token may have expired or been mistyped.",
347
354
},
348
355
"method_not_allowed": {
349
356
AgentAction: "Tell the user the HTTP method is wrong for this URL. Have them check the Allow response header (or https://instanode.dev/docs) for the supported methods.",
Copy file name to clipboardExpand all lines: internal/handlers/openapi.go
+3-3Lines changed: 3 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -2909,12 +2909,12 @@ const openAPISpec = `{
2909
2909
},
2910
2910
"StackRequest": {
2911
2911
"type": "object",
2912
-
"description": "Multipart form. The 'manifest' field is the YAML instant.yaml text; each service declared under services: must have a matching multipart field named after the service whose content is a gzipped tar archive of that service's build context.",
2912
+
"description": "Multipart form. The 'manifest' field is the YAML instant.yaml text; each service declared under services: must have a matching multipart field named after the service whose content is a gzipped tar archive of that service's build context. Codegen note: the dynamic per-service field is expressed via additionalProperties (OpenAPI cannot model literal-named fields whose names come from another field at runtime). Treat additionalProperties as: 'for every service S in manifest.services, send a multipart field named S whose value is the gzipped tar of S's build context.' DOG-30 (QA 2026-05-29).",
"name": { "type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[A-Za-z0-9][A-Za-z0-9 _-]*$", "description": "REQUIRED. Short human-readable label for this stack (1-64 chars after trimming; must start with a letter or digit, then letters/digits/spaces/underscores/hyphens). Missing/empty → 400 name_required. Bad format/length → 400 invalid_name." },
2916
-
"<service-name>": { "type": "string", "format": "binary", "description": "One field per service declared in the manifest, named after the service. Value is a gzipped tar archive containing that service's Dockerfile + source. Total request body cap is 200 MB." }
2915
+
"name": { "type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[A-Za-z0-9][A-Za-z0-9 _-]*$", "description": "REQUIRED. Short human-readable label for this stack (1-64 chars after trimming; must start with a letter or digit, then letters/digits/spaces/underscores/hyphens). Missing/empty → 400 name_required. Bad format/length → 400 invalid_name." }
2917
2916
},
2917
+
"additionalProperties": { "type": "string", "format": "binary", "description": "One multipart field per service declared in the manifest, with the field NAME equal to the real service name (e.g. 'api' or 'web', NOT the literal placeholder '<service-name>') and the VALUE a gzipped tar archive (≤50 MiB) containing that service's Dockerfile + source. Codegen clients should emit one upload field per manifest entry." },
// The literal placeholder must NOT appear as a property — it's the
159
+
// regression marker for the original bug. Codegen clients emitted a
160
+
// real field literally named "<service-name>" when this was present.
161
+
props, _:=schema["properties"].(map[string]any)
162
+
if_, has:=props["<service-name>"]; has {
163
+
t.Error("DOG-30: StackRequest.properties still contains the literal '<service-name>' placeholder — codegen clients will emit a broken field with that literal name. Use additionalProperties to express the dynamic per-service shape instead.")
164
+
}
165
+
166
+
// additionalProperties must be present with the binary-upload shape so
167
+
// codegen clients understand "for every service in manifest.services,
168
+
// emit one upload field named after the service".
169
+
ap, has:=schema["additionalProperties"]
170
+
if!has {
171
+
t.Fatal("DOG-30: StackRequest schema missing additionalProperties — the dynamic per-service multipart field has no machine-readable contract")
172
+
}
173
+
apMap, ok:=ap.(map[string]any)
174
+
if!ok {
175
+
t.Fatalf("DOG-30: StackRequest.additionalProperties must be an object schema, got %T", ap)
176
+
}
177
+
ifapMap["type"] !="string" {
178
+
t.Errorf("DOG-30: StackRequest.additionalProperties.type must be 'string' (multipart upload), got %v", apMap["type"])
179
+
}
180
+
ifapMap["format"] !="binary" {
181
+
t.Errorf("DOG-30: StackRequest.additionalProperties.format must be 'binary' (multipart binary upload), got %v", apMap["format"])
0 commit comments