{"record":{"id":"a568a8ad3fa97dc5","repo":"TryGhost/Ghost","slug":"failed-to-update-entitytype-response-status","errorCode":null,"errorMessage":"Failed to update ${entityType}: ${response.status()}","messagePattern":"Failed to update (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"e2e/data-factory/persistence/adapters/api.ts","lineNumber":77,"sourceCode":"        }\n\n        if (!response.ok()) {\n            throw new Error(`Failed to find ${entityType}: ${response.status()}`);\n        }\n\n        const body = await response.json() as TResponse;\n        return this.transformResponse(body) as T;\n    }\n\n    async update<T>(entityType: string, id: string, data: Partial<T>): Promise<T> {\n        const existing = await this.findById<T>(entityType, id);\n\n        const response = await this.httpClient.put(this.buildUrl(id), {\n            data: this.transformRequest({...existing, ...data} as unknown as TRequest)\n        });\n\n        if (!response.ok()) {\n            throw new Error(`Failed to update ${entityType}: ${response.status()}`);\n        }\n\n        const body = await response.json() as TResponse;\n        return this.transformResponse(body) as T;\n    }\n\n    async delete(entityType: string, id: string): Promise<void> {\n        const response = await this.httpClient.delete(this.buildUrl(id));\n\n        if (!response.ok() && response.status() !== 404) {\n            throw new Error(`Failed to delete ${entityType}: ${response.status()}`);\n        }\n    }\n}\n","sourceCodeStart":59,"sourceCodeEnd":92,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/e2e/data-factory/persistence/adapters/api.ts#L59-L92","documentation":"Thrown by ApiPersistenceAdapter.update() when the PUT to {endpoint}/{id} returns non-ok. update() first does a findById() (so 404 is already handled upstream as error 93) then merges and PUTs the full object; this throw fires only on the PUT itself failing. The message carries the status code but not the body.","triggerScenarios":"The merged payload fails server-side validation (e.g. a field constraint, a slug collision with another entity, an immutable field being changed); the test role lacks write permission (403); optimistic-concurrency conflict; or the Ghost server errored on the update.","commonSituations":"Factory update overwrote a unique field (slug, email) with a colliding value; the merged {...existing, ...data} included fields the API rejects on edit; permissions changed between create and update; another worker mutated the same record causing a conflict.","solutions":["Inspect the status code: 403 → permission; 422 → validation (check which field); 409 → conflict; 500 → server log.","Send only the fields you intend to change — the adapter merges onto the full existing record, so stray fields from build() can cause validation failures; consider narrowing the partial.","Confirm the test role has edit permission on the resource.","If it's a uniqueness collision, generate a unique value (e.g. random slug) in the partial.","Run 'pnpm test:types' to ensure the partial's shape matches the API contract."],"exampleFix":"// before: merges the entire built record, may carry fields invalid on edit\nawait factory.update(post.id, factory.build({status: 'published'}));\n\n// after: send only the changed field as a narrow partial\nawait factory.update(post.id, {status: 'published'});","handlingStrategy":"validation","validationCode":"// Send narrow partials — don't merge a full built record onto the update\nconst narrow = {status: 'published'}; // only the field(s) you intend to change\nawait factory.update(post.id, narrow);","typeGuard":null,"tryCatchPattern":"try {\n    return await factory.update(id, patch);\n} catch (err) {\n    const status = /Failed to update .*: (\\d+)/.exec(err.message)?.[1];\n    if (status === '422') throw new Error('Validation failed on update — check unique fields in the patch');\n    throw err;\n}","preventionTips":["Pass only changed fields to update(); the adapter already merges onto the fetched record.","Generate unique values for constrained fields (slug, email) when updating.","Confirm the test role has edit permission on the resource."],"tags":["e2e","data-factory","api-adapter","validation","test-infrastructure"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}