Skip to content
Xi Software

Diagnosing Why an Xi-Text Job Will Not Print

Every condition tested before a job reaches a printer, and why an unmatched job waits for ever, reporting nothing

Xi-TextXi-Textform-typesprint-queueprinterstroubleshootingsplistsqlist

A job sits in the Xi-Text queue and never prints. This article gives the tests the scheduler applies when it offers a job to a printer, in the order it applies them, and how to compare the job's attributes with the printers'.

How a job is chosen for a printer

The Xi-Text scheduler walks its own list of printers, and for each printer that is idle it walks the job queue from the front and takes the first job that passes every test. It then moves on to the next idle printer. The scan runs whenever anything happens to a job or a printer.

An Xi-Text job has no state and no status field. The queue holds a set of job attributes, and selection is a filter applied to them afresh on every scan. What you can observe about a job is its attributes and its position in the queue.

The scheduler writes nothing when it passes over a job. A job whose form type or class code matches no printer sits in the queue in its proper place for ever, looking like a job waiting its turn. The diagnosis is made by comparing the job's attributes with the printers' attributes, never by reading a log.

The queue is held in working-priority order, and sqlist lists it in that order, so a job's position in sqlist output is its position in the selection order.

The tests, in the order the scheduler applies them

These are the tests the scheduler applies, in the order it applies them. The first four decide whether a printer is offered any job at all; the rest are applied to each job in turn until one passes.

Scheduling is suspended
ssuspend stops the scheduler selecting any job on any printer until the period it was given expires or srelease is run. Every printer stays idle and the queue stops moving. Nothing in splist, sqlist or spq reports that scheduling is suspended, and nothing is written to the report file, so this has to be ruled out by asking.

The printer is not idle
only a printer displaying idle is offered a job. halted, offline, error, startup, printing, shutdown and a/w oper are all passed over. Visible in the state column of splist. See the article on printer states.

The printer has already been given a job
between the moment the scheduler selects a job and the moment the printer takes it, the printer is skipped. This clears itself within a scan and is never the explanation for a job that has waited for minutes.

The printer belongs to another host
a scheduler selects jobs only for its own printers. Printers on other hosts appear in splist as host:printer and are driven by that host's scheduler.

The job's copy count is zero
shown as 0 in the Cps column of sqlist. Such a job is accepted, queued and never printed. It is the very first test, so nothing else about the job matters. The usual causes are a job that has already printed and was retained in the queue, and a user whose permitted copy count is zero - spr sets the count to zero in that case and accepts the job.

The job is already assigned to a printer, or is being aborted
an assigned job is on its way to a printer, and its Printer column names one. A job being aborted disappears shortly afterwards.

A hold time has been set and has not yet arrived
the hold time is shown in the Hold column of sqlist (format letter %H), and the column is empty when no hold is set. This is the only thing in Xi-Text that corresponds to a job being "on hold"; there is no separate held flag.

The job is smaller than the printer's minimum size
the size is the whole job in bytes, disregarding any page range set on it. A minimum of zero accepts everything.

The job is larger than the printer's maximum size
a maximum of zero means no upper limit. A printer with limits set shows \< or > markers in spq.

The job came from another host and the printer is marked local-only
a local-only printer takes local jobs only. Where the licence does not cover networking, every printer is forced local-only, so on such a system no job from another host prints.

The class codes do not intersect
a class code is a 32-bit mask written as the letters A to P for the first sixteen bits and a to p for the rest, with a run of adjacent bits abbreviated as a range such as A-D. The job's code and the printer's code are ANDed, and if no bit is common to both the printer will not take the job. A job's class code is inherited from the submitting user's, so it is usually a property of the user.

The form type does not match the printer's paper type
the printer's form type is cut at its first . or -, and the job's form type must begin with that prefix, compared without regard to case, with the next character of the job's form type being either the end of the name or a separator. The printer's own suffix takes no part in the comparison. See the article on form types and paper types for the rule in full.

The job names a printer and this printer's name does not match it
the job's printer field is a pattern, not a name. It is matched against the printer's name without regard to case and understands *, ? and […], so lp* restricts a job to every printer whose name begins lp. An empty field means any printer.

Priority decides the order of the queue and takes no part in eligibility. A job at the back of the queue prints as soon as a printer becomes free that no job ahead of it can use.

The scheduler applies no permission test when it selects a job. Every privilege - the form types a user may request, the printers a user may name, the priority a user may set, the number of copies a user may take - is enforced by spr, sqchange and spq when the job is submitted or changed, and a refusal there is reported to the user with a message. A permission problem shows up as a job that was never accepted. The class code and the copy count that a user's privileges produce are carried on the job and are tested, which is why those two appear above.

Working through the tests

Step 1: read the job's fields

sqlist takes job numbers as arguments and restricts its listing to them, printing the same table it always prints. There is no per-job detail display on the command line.

# Every field that takes part in the selection decision
sqlist -H -F "%N %u %f %c %C %p %H %K %P" 15033

The fields are:

  • %N job number, %u owner
  • %f form type
  • %c copies - 0 here means the job will never print
  • %C class code, as letters
  • %p priority
  • %H hold time, empty when no hold is set
  • %K size in K
  • %P the printer it is printing on, or the printer pattern it asks for, or empty for any printer

A job you cannot see is still there. sqlist and spq hide every job whose class code does not intersect your own, and the -C option cannot widen the mask beyond your own class code unless you hold the override-class privilege, which is not usually given. If a user reports a job that no listing shows, compare class codes before concluding the job was never submitted.

Step 2: read the printers' fields

# The printer attributes that take part in the decision
splist -H -F "%p %d %f %s %c %y %z %j %u"

# Or open spq on the printer screen; o switches between the two screens
spq -p

The fields are the printer name, its device, its form type, its state, its class code, its minimum and maximum job sizes, and the job number and user it is printing. A printer in offline or error shows a reason after a colon where its filter or its device supplied one. splist also accepts printer names as arguments, and needs no privilege.

Step 3: match the form types

Only the part of the printer's form type before the first . or - is compared, and the comparison ignores case. A printer showing a4.ps accepts a job asking for a4, A4, a4.pcl or a4-p12, and rejects a4wide.

ptr1  <lp0>  a4.ps      idle       Match - this printer will take the job
ptr2  <lp1>  letter.ps  idle       No match
ptr3  <lp2>  a4.ps      halted     Match, but not idle, so not offered the job

The form type splist shows is the form type of the last job the printer took: the printer's stored form type is overwritten by each job it prints.

Step 4: find the job's position in the queue

With no job state to read, the question to answer is whether the job is being passed over or is behind others.

# The queue in selection order, unprinted jobs only
sqlist -H -y -F "%N %u %f %c %C %p %H %P"

Where jobs below your job in this listing are printing while it is not, the job is being rejected by one of the tests above, and the fields in the same listing say which. Where nothing is moving and every printer is idle, suspect suspended scheduling.

Step 5: read the report file

tail -50 /var/spool/xi/spd/spshed_reps

The report file receives printer startup and setup-file failures, device and filter errors, and the scheduler's own fatal errors. Nothing about job selection is written here, and a clean system writes nothing at all. Read this file to find out why a printer is in error. The same file is shown by X in spq.

The spool directory is the compiled-in default above; on a system where it has been moved, take the real value from SPOOLDIR in /etc/xi/textconfig.

Resolving each case

Scenario 1: no printer carries the job's form type

Symptom:

The job sits in the queue indefinitely and no printer's paper type matches it.

sqlist -N -F "%N %f" 15033
# 15033 labels.ps

splist -N -F "%p %f %s"
# ptr1 a4.ps      idle
# ptr2 letter.ps  idle
# no printer whose paper type is labels

Option A: change the job's form type

sqchange -f a4.ps 15033

The job prints on ptr1 as soon as the scheduler next scans. Changing another user's job needs the "other jobs" privilege, and the new form type must be one the user is allowed to request.

Option B: change the printer's form type

spstart ptr1 labels.ps

One command is enough: given a paper type that differs from the printer's, spstart halts the printer, changes the form type and starts it again. Use spchange ptr1 labels.ps for a printer that is to stay halted. Both need the add/delete-printers privilege, which ordinary users do not have by default.

Option C: add a printer for the form type

spadd -l /dev/lp2 labels-ptr labels.ps
spstart labels-ptr

The device is given with -l and is required; the printer name and the form type are positional arguments in that order. A new printer is created halted and has to be started.

Scenario 2: every printer is halted

Symptom:

splist -N -F "%p %s"
# every printer shows halted, and jobs accumulate

This is the normal state of every printer after the spooler has been restarted: the scheduler sets every printer it reads from the saved printer list to halted, whatever state it was in before, and does not start any of them.

Solution:

# Start one printer
spstart ptr1

# Start every printer - the name is a pattern, and -f is required
# whenever the pattern matches more than one printer
spstart -f '*'

spstart with no printer name at all starts the spooler, not the printers. A start that fails is not reported to the caller: spstart returns success because the printer is started by the scheduler in a separate process, and the reason for the failure appears only in spshed_reps. Confirm with splist, or scriptably with spstat.

Scenario 3: the job is restricted to a printer that is unavailable

Symptom:

sqlist -N -F "%N %P" 15033
# 15033 ptr2

splist -N -F "%p %s" ptr2
# ptr2 halted

Option A: remove the restriction and let the scheduler choose

sqchange -P - 15033

A bare - clears the field. The job then prints on any idle printer whose paper type and class code match.

Option B: start the printer the job asks for

spstart ptr2

Option C: point the job at a different printer

sqchange -P ptr1 15033

The value is a pattern, so sqchange -P 'ptr[13]' 15033 widens the job to two printers rather than moving it to one. A user without the "other printers" privilege may only name printers matching the pattern recorded against them.

Scenario 4: the job has a hold time

Symptom:

The job does not advance although a suitable printer is idle, and the Hold column shows a time in the future.

sqlist -H -F "%N %u %H" 15033

Solution:

sqchange -n - 15033

A bare - to -n clears the hold time and the job becomes eligible on the next scan. sqchange -n 0 also releases the job, but it sets the hold to the current instant rather than removing it, so the Hold column goes on showing a time in the past. In spq the hold time is the H key on the job screen. Find out why the job was held before clearing it.

Scenario 5: the class codes do not intersect

Symptom:

A printer with a matching paper type is idle and the job still does not print.

sqlist -N -F "%N %C" 15033
# 15033 A

splist -N -F "%p %c" ptr1
# ptr1 B

A class code is a 32-bit mask written as letters - A to P for bits 1 to 16 and a to p for bits 17 to 32 - and adjacent bits are abbreviated as a range, so a printer that accepts everything shows A-Pa-p. The test is a bitwise AND. Here the job carries only A and the printer only B, so they have no bit in common and the printer will not take the job. Codes AB and BC do intersect, in B. spq shows the same value as 32 columns with a full stop for each clear bit.

Option A: change the job's class code

sqchange -C B 15033

A user without the class-override privilege can only set bits that are already in their own class code, and a value that leaves nothing is refused. The letter names a bit position: anything after the first character that is not a letter in the range A to P or a to p, a range separator or a full stop is discarded silently.

Option B: change the printer's class code

spchange -S AB ptr1

Option C: let the printer take everything

spchange -S A-Pa-p ptr1

Both need the add/delete-printers privilege. In spq the printer's class code is the C key and the printer must be halted first; the command line does not require that.

Scenario 6: the job is behind others in the queue

Symptom:

Other jobs print first.

The queue is ordered by working priority, which is derived from the job's priority when the job is enqueued. A new job is inserted from the back of the queue forwards, past every job of lower priority, and its working priority is reduced by a fixed decrement for each job it passes, so a high-priority job joining a long queue of lower-priority jobs stops part way up it. The decrement is normally 1 and is set when the spooler is started.

sqlist -H -y -F "%N %u %p %f" 

Solution:

sqchange -p 200 15033

The job is taken off the queue and re-inserted under the same rule, so the effect of a priority increase depends on what is ahead of it. Priorities run from 1 to 255. Changing a priority needs the change-priority privilege, which is not in the default privilege set, and without the any-priority privilege the value must lie between the user's own minimum and maximum - 100 and 200 for a default user, whose default priority is 150.

Scenario 7: the job is outside the printer's size limits

Symptom:

A printer with a matching paper type and class code is idle and still does not take the job.

splist -H -F "%p %y %z" ptr1

The two fields are the minimum and maximum job size the printer will accept, in bytes. The size compared is the whole job, disregarding any page range set on it. Zero in either field means no limit in that direction.

Solution:

In spq, on the printer screen, L sets the lower limit and U the upper; entering 0 removes the limit. The printer must be halted and the halt/start privilege is required. A printer with a limit in force is marked \< or > on the printer screen.

Scenario 8: the job has zero copies

Symptom:

The job is in the queue, every printer attribute matches, and it never prints.

sqlist -N -F "%N %c" 15033
# 15033 0

Zero copies is the first test the scheduler applies, and nothing about the job is examined after it. A job that has already printed and was retained in the queue shows zero here and is expected to. A job that has never printed and shows zero was submitted by a user whose permitted copy count is zero, in which case spr accepted the job and set the count to zero instead of refusing it.

Solution:

sqchange -c 1 15033

Raise the user's permitted copy count as well, or every job they submit will do the same thing.

Checklist, in the order the scheduler applies the tests

1. The system

  • Scheduling has not been suspended with ssuspend
  • At least one printer is in the idle state and belongs to this host

2. The job

  • Copies is not 0
  • The job is not already assigned to a printer
  • No hold time is set, or it has passed

3. The job against the printer

  • The job size is within the printer's minimum and maximum
  • A remote job is not being offered a local-only printer
  • The class codes share at least one bit
  • The paper type - the part before the first . or - - matches
  • The job's printer pattern, if it has one, matches the printer's name

4. If all of the above pass and the job still waits

  • Check that jobs are being selected at all: is anything printing anywhere?
  • Check the report file for a printer that is failing to start, which leaves the printer out of idle and therefore out of the running

Confirming the job prints

Selection happens within a scan, so the effect of a change is visible at once.

# The queue, and the printers, side by side
sqlist -H -y
splist -H

The job should show a printer name in its Printer column, that printer should show the job number in splist and be in the printing state, and the job should leave the queue when it completes unless it was submitted to be retained.

spq shows both screens and refreshes itself; -R sets the interval.

Keeping jobs printable

Leave the job's printer field empty unless it has to be set. A job that names no printer can be taken by any printer whose paper type and class code suit it.

Only the paper type takes part in matching, so paper types are the names that have to agree across printers; suffixes are free to differ.

A class code is an absolute barrier. A job whose class code intersects no printer's waits for ever with no indication of why, so keep class codes for access and let the paper type express where a job should go.

Restarting the spooler leaves every printer halted, so a startup script that starts the spooler starts the printers too, with spstart -f '*'.

Watch the length of the unprinted queue:

sqlist -N -y | wc -l

-N suppresses the heading and -y restricts the listing to jobs that have not printed, so the count is the number of jobs still waiting. Jobs outside your own class code are left out, because they are not shown.

A job submitted with a paper type no printer carries is accepted and then waits, with no message to the user who sent it. Publish the list of paper types in use.

Configuring Xi-Text Form Types, Paper Types and Suffixes

What a form type is, the exact rule matching a job to a printer, and how a suffix changes without stopping the printer

Identifying and Removing Redundant Xi-Text Jobs and Printers

What the queue deletes by itself, why a printer's state is no evidence of disuse, and the two-stage removal

Determining When an Xi-Text Printer Was Last Used

Xi-Text records no last-used time, so the only evidence is the optional per-printer log - what it holds and omits

All articles · Release notes · Contact support