You enumerate the camera's features, call the write for gain or exposure, and your client refuses: the attribute reports not writable. The same value edits fine in a tree browser, which makes it look like a bug in your code. Most of the time it is not. The feature's mode selector is sitting at Auto or One push, and while that selector is engaged the camera owns the value register, so the camera's own description marks the value node read-only. This guide gives you the causes in test order, the set-mode-first fix with a worked example, and the initialization order that keeps the write alive after every power cycle.
The symptom: read works, write is refused
The pattern is specific. Reading the value node returns a plausible number, so the link, the driver and the feature itself are alive. The write fails with a read-only or not-writable error, or the node sits greyed out in the tree. Your own enumeration of camera attributes lists the value as not writable. Then a browser tool edits the same value without complaint, which is the strongest clue you have: a human-facing tool succeeds where your write is refused, usually because it drives the selector as part of its edit flow.
This is a different failure from a feature that reads as locked or missing outright. There the node itself lacks write access in the camera's access table; here the node is healthy and the mode is doing the blocking.
Why a mode selector gates a value node
A controllable feature on a GenICam camera is a pair: a selector that chooses who drives the register, and the value itself. While Auto runs, the camera rewrites the register frame by frame, so a client write would be overwritten before the next exposure. The camera's description therefore advertises the value as read-only. Set the selector to Manual and ownership passes to your code, and the same node accepts writes.
Three states matter, and older IEEE 1394 (DCAM/IIDC) cameras and many current camera manuals name them the same way:
| Selector setting | Who drives the value | Value node writable? |
|---|---|---|
| Manual | Your client | Yes |
| Auto (continuous) | The camera, every frame | No |
| One push | The camera, during the one-shot run | No while it runs; many cameras return to Manual afterwards |
| Ignored (legacy DCAM trees) | Nobody; the feature is switched off | No |
Current GenICam trees fold the same three states into a single auto selector and rename the entries, so the exact symbolics differ by camera family. Read them from the camera itself before you write them. The gating works the same across interfaces, and a 6 MP mono USB3.0 model carries the same selector-over-value structure:
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 6-10 MP
Cause 1: the selector is still in Auto (most common)
Test: before any write, read the selector's current symbolic and print it next to the value. If it reads Auto, you have the answer already.
Fix: write the Manual entry. Set it by the exact symbolic string the camera reports, not by an index you assumed; if you must map integers, build the map from the camera's own enumeration list, the way the articles on GenICam enumeration symbolics and setting an enumeration by integer value walk through. If you cannot find the selector at all, discover the feature set first: the procedure for discovering the exposure feature before acquisition applies to the whole gain group too.
To reproduce the failure and then prove the fix on a bench, any compact GigE camera will do; this one pairs a 2.5 MP global-shutter sensor with GigE and a C-mount:
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Interface
- GigE
- Resolution
- Up to 3 MP
Cause 2: One push is mid-run or left engaged
During a one-push operation - one-push white balance is the classic - the camera drives the register and the value node locks again. Several camera families run the one-shot once and drop the selector back to Manual on completion, at which point the value is yours again; others keep a dedicated one-push entry until you move the selector yourself. Poll the selector until it reads Manual instead of sleeping a fixed number of milliseconds, then write the value. If the poll stalls, write Manual explicitly and continue: the operation has finished or was never triggered.
Cause 3: a second client holds the camera
When every feature's value refuses, not just gain, the mode is probably innocent. A viewer left open, a second process on the host, or the same camera opened from two machines all produce read-only trees. Close the other clients, re-enumerate, and compare; if the single feature becomes writable after closing them, go back to Cause 1. In production, avoid the shared-office-PC pattern entirely: give the client its own machine beside the line, which is the job a machine-vision controller does. One with 5+ LAN ports, 3-4 USB 3 ports, 4 built-in light channels and 8 GB of memory holds the camera, the light driver and the inspection code in one place:
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
Cause 4: the node is constant or privilege-gated
Some nodes are computed or constant - the value the auto logic last applied, for example - and no selector change makes them writable. Camera feature references print an access mode per feature, and some entries read as read-only with a note that they become read/write at a higher user privilege level. Two tests separate these cases. First, put a sibling feature (exposure) in Manual and write its value: if the sibling passes, the problem belongs to your node, not the camera. Second, if the same node turns writable in a browser tool only after a login, your client must establish that privilege level through the camera's documented mechanism before it writes.
Worked example: freeing the gain value on a GigE camera streaming at 30 fps
Inputs, with their sources:
- Auto selector for gain, read from the camera tree at connect:
Auto(typical case). - Value node writable flag from your attribute enumeration:
false(the symptom that started the search). - Target gain for the backlit part in view: 6 dB (chosen for this example's application).
- Frame rate: 30 fps (typical GigE streaming rate for this sensor size).
- Settle window between the mode write and the value write: 10 frames (chosen acceptance).
- Acceptances: readback error at most 0.1 dB; brightness drift under 2 counts on a 0-255 scale over the settle window.
Rule in words: the value node accepts writes only while the selector reads Manual; after switching, let the camera apply the state, then write and confirm by readback and by image statistics.
Arithmetic: 1 / 30 fps = 0.033 s per frame, so 10 frames settle in 10 x 33 ms = 0.33 s. Reproduction first: with the selector at Auto, the write is refused and the writable flag reads false, which confirms the mode is the blocker. Then: write Manual, wait 0.33 s, write 6 dB, read back 6.0 dB. Error = abs(6.0 - 6) = 0.0 dB, inside the 0.1 dB tolerance. The mean grey level over the same 10 frames moves 1 count against the previous 10, under the 2-count acceptance, so the value is holding.
Decision: the fix passes when the readback error is at most 0.1 dB and the mean grey level moves fewer than 2 counts over the 0.33 s settle window. If the readback error exceeds 0.1 dB, or the mean keeps moving after the settle window, the selector did not take: re-read its symbolic before writing anything else.
The fix, in order
- Read the selector's symbolic for the feature and log it with a timestamp.
- Write the Manual entry, by exact symbolic or by an integer mapped from the camera's own enumeration.
- Wait the settle window - 10 frames, 0.33 s at 30 fps in the worked example.
- Write the value through the documented feature - 6 dB there, never as a raw register poke; the write-then-readback pattern in setting exposure time through a GenICam API in Python transfers directly to gain.
- Read the value back and accept only when the error is within tolerance.
- Store the selector symbolic and the value together in your settings, and restore them in that order at every start.
Prevention: set every mode before you stream
- Order the initialization: selectors to Manual first, values second, stream last. The walkthrough of setting manual exposure before streaming shows why the order matters for the first frames.
- Leave nothing on Auto in production. Auto logic re-locks the values you thought you owned and lets brightness follow the scene; the failure behind camera brightness drifting over time usually starts exactly here.
- Confirm by readback: a refused write may surface as an exception, a false return or nothing at all. Read the value node after every write, and check a node's read state before trusting a readback, as in the article on camera feature node readability.
- Keep one writer per camera for the camera's whole life, tools included.
- If the camera offers saved user sets, write both the selector and the value there, so the camera boots into the state your code expects.
When the mode is Manual and it still refuses
Escalate in this order:
- Access state: a stale handle or another client holds the camera and every node reports read-only. The article on a GenICam node that is not writable walks the access-state checks.
- Privilege: the feature's access mode prints read-only until a higher privilege level is established.
- Wrong node: you are writing a computed or constant entry; write the manual control node instead.
- Genuinely fixed value: some entries have no manual path at all. Treat the readback as telemetry and move on.
The mode-blocks-write pattern reaches past cameras too: an OPC-UA tag reports read-only until its own tag mode allows writes, and the writable tag mode fix guide for OPC-UA tags walks that case if your inspection results also flow through a SCADA server.
Where to go next
If you are standing up the station that keeps hitting this, start from the area scan cameras and the camera finder, or go straight to the camera step of the configurator; the vision controllers page covers the one-writer host. The systems topic page collects the rest of the GenICam checks, from enumeration symbolics to initialization order.
Questions
Why can the vendor's browser edit the gain when my code is refused?
The browser drives the selector as part of its edit flow: when you type a new gain there, it sets the mode to Manual for you, then writes the value, then confirms by readback. Your code skipped step one, so the value stayed read-only. Reproduce the browser's order - mode, then value, then readback - inside your own client and the write goes through.
Can I write the gain register directly instead of setting the mode first?
You can, and it will fail the same way or worse. While auto logic runs, it rewrites that register every frame, so a raw write reverts within one exposure, and it can also collide with the camera's privilege checks. The documented path - set the selector, then write the feature through the API - is the one that holds.
Does One push leave the camera in Manual afterwards?
Often, not always. Several camera families run the one-shot once and return the selector to Manual, which frees the value node; others hold a dedicated one-push entry until you change the selector yourself. Never assume either behavior: poll the selector and write the value only once it reads Manual, and write Manual explicitly if the poll stalls.
Which camera features carry a mode selector that can block writes?
The usual suspects are gain, exposure, white balance and any motorized lens control such as focus or iris, because all of them have an auto mode the camera can run. Trigger and synchronization settings gate differently and usually refuse only through access state. Check the selector of any feature whose write suddenly fails after auto behavior was enabled.
How do I keep the write working after every power cycle?
Apply the same order at every start: read the selector, write Manual, wait the settle window, write the value, read it back. Store the selector symbolic and the value together in your settings so a restore cannot apply the value while the selector still reads Auto. If the camera offers saved user sets, write both there as well.


